{ "info": { "author": "Oleg Churkin", "author_email": "bahusoff@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 5 - Production/Stable", "Environment :: Web Environment", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Topic :: Software Development :: Libraries :: Python Modules" ], "description": "# UWSGI Tasks engine\n\nThis package makes it to use [UWSGI signal framework](http://uwsgi-docs.readthedocs.org/en/latest/Signals.html)\nfor asynchronous tasks management. It's more functional and flexible than [cron scheduler](https://wikipedia.org/wiki/Cron), and\ncan be used as replacement for [celery](http://www.celeryproject.org/) in many cases.\n\n## Requirements\n\nThe module works only in [UWSGI web server](https://uwsgi-docs.readthedocs.org/en/latest/) environment,\nyou also may have to setup some [mules](https://uwsgi-docs.readthedocs.org/en/latest/Mules.html) or\\and [spooler processes](http://uwsgi-docs.readthedocs.org/en/latest/Spooler.html) as described in UWSGI documentation.\n\n## Installation\n\nSimple execute `pip install uwsgi_tasks`\n\n## Usage\n\n### Mules, farms and spoolers\n\n**Use case**: you have Django project and want to send all emails asynchronously.\n\nSetup some mules with `--mule` or `--mules=` parameters, or some spooler\nprocesses with `--spooler==`.\n\nThen write:\n\n```python\n# myapp/__init__.py\nfrom django.core.mail import send_mail\nfrom uwsgi_tasks import task, TaskExecutor\n\n@task(executor=TaskExecutor.SPOOLER)\ndef send_email_async(subject, body, email_to):\n # Execute task asynchronously on first available spooler\n return send_mail(subject, body, 'noreply@domain.com', [email_to])\n\n...\n\ndef my_view():\n # Execute tasks asynchronously on first available spooler\n send_email_async('Welcome!', 'Thank you!', 'user@domain.com')\n```\n\nExecution of `send_email_async` will not block execution of `my_view`, since\nfunction will be called by first available spooler. I personally recommend to use spoolers rather than mules for several reasons:\n\n1. Task will be executed\\retried even if uwsgi is crashed or restarted, since task information stored in files.\n2. Task parameters size is not limited to 64 KBytes.\n3. You may switch to external\\network spoolers if required.\n4. You are able to tune task execution flow with introspection facilities.\n\n\nThe following tasks execution backends are supported:\n\n* `AUTO` - default mode, spooler will be used if available, otherwise mule will be used. If mule is not available, than task is executed at runtime.\n* `MULE` - execute decorated task on first available mule\n* `SPOOLER` - execute decorated task on spooler\n* `RUNTIME` - execute task at runtime, this backend is also used in case `uwsgi` module can't be imported, e.g. tests.\n\nCommon task parameters are:\n\n* `working_dir` - absolute path to execute task in. You won't typically need to provide this value, since it will be provided automatically: as soon as you execute the task current working directory will be saved and sent to spooler or mule. You may pass `None` value to disable this feature.\n\nWhen `SPOOLER` backend is used, the following additional parameters are supported:\n\n* `priority` - **string** related to priority of this task, larger = less important, so you can simply use digits. `spooler-ordered` uwsgi parameter must be set for this feature to work (in linux only?).\n* `at` - UNIX timestamp or Python **datetime** or Python **timedelta** object \u2013 when task must be executed.\n* `spooler_return` - boolean value, `False` by default. If `True` is passed, you can return spooler codes from function, e.g. `SPOOL_OK`, `SPOOL_RETRY` and `SPOOL_IGNORE`.\n* `retry_count` - how many times spooler should repeat the task if it returns `SPOOL_RETRY` code, implies `spooler_return=True`.\n* `retry_timeout` - how many seconds between attempts spooler should wait to execute the task. Actual timeout depends on `spooler-frequency` parameter. Python **timedelta** object is also supported.\n\n**Use case**: run task asynchronously and repeat execution 3 times at maximum if it fails, with 5 seconds timeout between attempts.\n\n```python\nfrom functools import wraps\nfrom uwsgi_tasks import task, TaskExecutor, SPOOL_OK, SPOOL_RETRY\n\ndef task_wrapper(func):\n @wraps(func) # required!\n def _inner(*args, **kwargs):\n print 'Task started with parameters:', args, kwargs\n try:\n func(*args, **kwargs)\n except Exception as ex: # example\n print 'Exception is occurred', ex, 'repeat the task'\n return SPOOL_RETRY\n\n print 'Task ended', func\n return SPOOL_OK\n\n return _inner\n\n@task(executor=TaskExecutor.SPOOLER, retry_count=3, retry_timeout=5)\n@task_wrapper\ndef spooler_task(text):\n print 'Hello, spooler! text =', text\n raise Exception('Sorry, task failed!')\n```\n\nRaising `RetryTaskException(count=, timeout=)` approach can be also used to retry task execution:\n\n```python\nimport logging\nfrom uwsgi_tasks import RetryTaskException, task, TaskExecutor\n\n@task(executor=TaskExecutor.SPOOLER, retry_count=2)\ndef process_purchase(order_id):\n\n try:\n # make something with order id\n ...\n except Exception as ex:\n logging.exception('Something bad happened')\n # retry task in 10 seconds for the last time\n raise RetryTaskException(timeout=10)\n```\n\nBe careful when providing `count` parameter to the exception constructor - it may lead to infinite tasks execution, since the parameter replaces the value of `retry_count`.\n\nTask execution process can be also controlled via spooler options, see details [here](http://uwsgi-docs.readthedocs.org/en/latest/Spooler.html?highlight=spool_ok#options).\n\n### Project setup\n\nThere are some requirements to make asynchronous tasks work properly. Let's imagine your Django project has the following directory structure:\n\n```\n\u251c\u2500\u2500 project/\n\u2502 \u251c\u2500\u2500 venv/ <-- your virtual environment is placed here\n\u2502 \u251c\u2500\u2500 my_project/ <-- Django project (created with \"startproject\" command)\n\u2502 \u2502 \u251c\u2500\u2500 apps/\n\u2502 \u2502 \u2502 \u251c\u2500\u2500 index/ <-- Single Django application (\"startapp\" command)\n\u2502 \u2502 \u2502 \u2502 \u251c\u2500\u2500 __init__.py\n\u2502 \u2502 \u2502 \u2502 \u251c\u2500\u2500 admin.py\n\u2502 \u2502 \u2502 \u2502 \u251c\u2500\u2500 models.py\n\u2502 \u2502 \u2502 \u2502 \u251c\u2500\u2500 tasks.py\n\u2502 \u2502 \u2502 \u2502 \u251c\u2500\u2500 tests.py\n\u2502 \u2502 \u2502 \u2502 \u251c\u2500\u2500 views.py\n\u2502 \u2502 \u2502 \u251c\u2500\u2500 __init__.py\n\u2502 \u2502 \u251c\u2500\u2500 __init__.py\n\u2502 \u2502 \u251c\u2500\u2500 settings.py\n\u2502 \u2502 \u251c\u2500\u2500 urls.py\n\u2502 \u251c\u2500\u2500 spooler/ <-- spooler files are created here\n```\n\nMinimum working UWSGI configuration is placed in `uwsgi.ini` file:\n\n```ini\n[uwsgi]\nhttp-socket=127.0.0.1:8080\nprocesses=1\nworkers=1\n\n# python path setup\nmodule=django.core.wsgi:get_wsgi_application()\n# absolute path to the virtualenv directory\nvenv=/project/venv/\n# Django project directory is placed here:\npythonpath=/project/\n# \"importable\" path for Django settings\nenv=DJANGO_SETTINGS_MODULE=my_project.settings\n\n# spooler setup\nspooler=/project/spooler\nspooler-processes=2\nspooler-frequency=10\n```\n\nIn such configuration you should put the following code into `my_project/__init__.py` file:\n\n```python\n# my_project/__init__.py\nfrom uwsgi_tasks import set_uwsgi_callbacks\n\nset_uwsgi_callbacks()\n```\n\nTask functions (decorated with `@task`) may be placed in any file where they can be imported, e.g. `apps/index/tasks.py`.\n\nIf you still receive some strange errors when running asynchronous tasks, e. g.\n\"uwsgi unable to find the spooler function\" or \"ImproperlyConfigured Django exception\", you may try\nthe following: add to uwsgi configuration new variable `spooler-import=my_project` - it will force spooler\nto import `my_project/__init__.py` file when starting, then add Django initialization\ninto this file:\n\n```python\n# my_project/__init__.py\n# ... set_uwsgi_callbacks code ...\n\n# if you use Django, otherwise use\n# initialization related to your framework\\project\nfrom uwsgi_tasks import django_setup\n\ndjango_setup()\n```\n\nAlso make sure you **didn't override** uwsgi callbacks with this code\n`from uwsgidecorators import *` somewhere in your project.\n\nIf nothing helps - please submit an issue.\n\nIf you want to run some cron or timer-like tasks on project initialization you\nmay import them in the same file:\n\n```python\n# my_project/__init__.py\n# ... set_uwsgi_callbacks\n\nfrom my_cron_tasks import *\nfrom my_timer_tasks import *\n```\n\nKeep in mind that task arguments must be [pickable](http://stackoverflow.com/questions/3603581/what-does-it-mean-for-an-object-to-be-picklable-or-pickle-able), since they are serialized and send via socket (mule) or file (spooler).\n\n### Timers, red-black timers and cron\n\nThis API is similar to uwsgi bundled Python decorators [module](http://uwsgi-docs.readthedocs.org/en/latest/PythonDecorators.html). One thing to note: you are not able to provide any arguments to timer-like or cron-like tasks. See examples below:\n\n```python\nfrom uwsgi_tasks import *\n\n@timer(seconds=5)\ndef print_every_5_seconds(signal_number):\n \"\"\"Prints string every 5 seconds\n\n Keep in mind: task is created on initialization.\n \"\"\"\n print 'Task for signal', signal_number\n\n@timer(seconds=5, iterations=3, target='workers')\ndef print_every_5_seconds(signal_number):\n \"\"\"Prints string every 5 seconds 3 times\"\"\"\n print 'Task with iterations for signal', signal_number\n\n@timer_lazy(seconds=5)\ndef print_every_5_seconds_after_call(signal_number):\n \"\"\"Prints string every 5 seconds\"\"\"\n print 'Lazy task for signal', signal_number\n\n@cron(minute=-2)\ndef print_every_2_minutes(signal_number):\n print 'Cron task:', signal_number\n\n@cron_lazy(minute=-2, target='mule')\ndef print_every_2_minutes_after_call(signal_number):\n print 'Cron task:', signal_number\n\n...\n\ndef my_view():\n print_every_5_seconds_after_call()\n print_every_2_minutes_after_call()\n```\n\nTimer and cron decorators supports `target` parameter, supported values are described [here](http://uwsgi-docs.readthedocs.org/en/latest/PythonModule.html#uwsgi.register_signal).\n\nKeep in mind the maximum number of timer-like and cron-like tasks is 256 for each available worker.\n\n### Task introspection API\n\nUsing task introspection API you can get current task object inside current task function and will be able to change some task parameters. You may also use special `buffer` dict-like object to pass data between task execution attempts. Using `get_current_task` you are able to get internal representation of task object and manipulate the attributes of the task, e.g. SpoolerTask object has the following changeable properties: `at`, `retry_count`, `retry_timeout`.\n\nHere is a complex example:\n\n```python\nfrom uwsgi_tasks import get_current_task\n\n@task(executor=TaskExecutor.SPOOLER, at=datetime.timedelta(seconds=10))\ndef remove_files_sequentially(previous_selected_file=None):\n # get current SpoolerTask object\n current_task = get_current_task()\n\n selected_file = select_file_for_removal(previous_selected_file)\n\n # we should stop the task here\n if selected_file is None:\n logger.info('All files were removed')\n for filename, removed_at in current_task.buffer['results'].items():\n logger.info('File \"%s\" was removed at \"%s\"', filename, removed_at)\n for filename, error_message in current_task.buffer['errors'].items():\n logger.info('File \"%s\", error: \"%s\"', filename, error_message)\n return\n\n try:\n logger.info('Removing the file \"%s\"', selected_file)\n # ... remove the file ...\n del_file(selected_file)\n except IOError as ex:\n logger.exception('Cannot delete file \"%s\"', selected_file)\n\n # let's try to remove this one more time later\n io_errors = current_task.buffer.setdefault('errors', {}).get(selected_file)\n if not io_errors:\n current_task.buffer['errors'][selected_file] = str(ex)\n current_task.at = datetime.timedelta(seconds=20)\n return current_task(previous_selected_file)\n\n # save datetime of removal\n current_task.buffer.setdefault('results', {})[selected_file] = datetime.datetime.now()\n\n # run in async mode\n return current_task(selected_file)\n```\n\n#### Changing task configuration before execution\n\nYou may use `add_setup` method to change some task-related settings before (or during) task execution process. The following example shows how to change timer's timeout and iterations amount at runtime:\n\n```python\nfrom uwsgi_tasks import timer_lazy\n\n@timer_lazy(target='worker')\ndef run_me_periodically(signal):\n print('Running with signal:', signal)\n\ndef my_view(request):\n run_me_periodically.add_setup(seconds=10, iterations=2)\n run_me_periodically()\n```\n\n\n", "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/Bahus/uwsgi_tasks", "keywords": "asynchronous,tasks,uwsgi", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "uwsgi-tasks", "package_url": "https://pypi.org/project/uwsgi-tasks/", "platform": "Platform Independent", "project_url": "https://pypi.org/project/uwsgi-tasks/", "project_urls": { "Homepage": "https://github.com/Bahus/uwsgi_tasks" }, "release_url": "https://pypi.org/project/uwsgi-tasks/0.7.3/", "requires_dist": [ "six" ], "requires_python": "", "summary": "Asynchronous tasks management with UWSGI server", "version": "0.7.3" }, "last_serial": 5396118, "releases": { "0.1": [], "0.2": [ { "comment_text": "", "digests": { "md5": "c85f14236a6c7080972370a4c330a22f", "sha256": "99e7c56f89ac1ae440ddd85a876a3072447d8801f3e7304acb8462b3781c5df5" }, "downloads": -1, "filename": "uwsgi-tasks-0.2.tar.gz", "has_sig": false, "md5_digest": "c85f14236a6c7080972370a4c330a22f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 9795, "upload_time": "2015-04-20T18:59:00", "url": "https://files.pythonhosted.org/packages/37/73/e377382a1309313c31049c24203a4b08c97b91665504a43429485b4d8f97/uwsgi-tasks-0.2.tar.gz" } ], "0.3": [ { "comment_text": "", "digests": { "md5": "7b9e78bd7c38f5d9a23ce11935a07697", "sha256": "60b08470f5cd16c66510764d2b5605bfc27b1094025075854e77a084eecd073d" }, "downloads": -1, "filename": "uwsgi-tasks-0.3.tar.gz", "has_sig": false, "md5_digest": "7b9e78bd7c38f5d9a23ce11935a07697", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 10296, "upload_time": "2015-04-22T11:23:42", "url": "https://files.pythonhosted.org/packages/6e/a4/e4728257a379a81089ae03bf907d7143a603364dd64664de3e437fb0eddc/uwsgi-tasks-0.3.tar.gz" } ], "0.4": [ { "comment_text": "", "digests": { "md5": "88845397b602e85ae822981d255322fd", "sha256": "a0ab3ae753778dd52949f94386fc32023b73bc2f1e3fe78ebcaa8a2676b22a2c" }, "downloads": -1, "filename": "uwsgi-tasks-0.4.tar.gz", "has_sig": false, "md5_digest": "88845397b602e85ae822981d255322fd", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 11121, "upload_time": "2015-05-02T18:06:25", "url": "https://files.pythonhosted.org/packages/1f/eb/dd148a06cda56e6926f03254f102b54f65a25843b9a1153cc8b4f5fb0549/uwsgi-tasks-0.4.tar.gz" } ], "0.4.1": [ { "comment_text": "", "digests": { "md5": "4e772bdbda8e0c159893e9a4a03ad2e7", "sha256": "5dce3f618c4936f3861b86a44ae2cc8d6ea1aa0719bb3e32e9fc83c639ed5384" }, "downloads": -1, "filename": "uwsgi-tasks-0.4.1.tar.gz", "has_sig": false, "md5_digest": "4e772bdbda8e0c159893e9a4a03ad2e7", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 11135, "upload_time": "2015-05-02T18:19:24", "url": "https://files.pythonhosted.org/packages/79/a7/18fb0cc268263fb7d135e0e86cb8a4689a824789cdf937cb479ae598da2e/uwsgi-tasks-0.4.1.tar.gz" } ], "0.4.2": [ { "comment_text": "", "digests": { "md5": "42203edfbecab6c82ea1ccf1fef089ae", "sha256": "76cef9b6b60e22498bd00cdeac2dffb649e232d63cb6bf6d47b076e6e08952c7" }, "downloads": -1, "filename": "uwsgi-tasks-0.4.2.tar.gz", "has_sig": false, "md5_digest": "42203edfbecab6c82ea1ccf1fef089ae", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 11137, "upload_time": "2015-05-02T18:22:17", "url": "https://files.pythonhosted.org/packages/5b/7a/8a8c20c9aae92624ee4a55462fa79e0214772615e30785112ff1f52be2d2/uwsgi-tasks-0.4.2.tar.gz" } ], "0.4.3": [ { "comment_text": "", "digests": { "md5": "6e6b636c4e831f59159d688608424148", "sha256": "dea07618be36f4fb9c42164f8ec75ad8cdf690b8a2e076c94fca8ad6619a8784" }, "downloads": -1, "filename": "uwsgi-tasks-0.4.3.tar.gz", "has_sig": false, "md5_digest": "6e6b636c4e831f59159d688608424148", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 11145, "upload_time": "2015-05-02T18:35:30", "url": "https://files.pythonhosted.org/packages/52/f5/f17f0106fa17d051f2d0087b7a779d0b05859ff9ca5c90f36efa00f49468/uwsgi-tasks-0.4.3.tar.gz" } ], "0.4.4": [ { "comment_text": "", "digests": { "md5": "7bfe858414f14a1ebbca9d05480f7404", "sha256": "56d36eee6128dd3ae721972175abdb44b3f0fa801640933f52ff10700b68387d" }, "downloads": -1, "filename": "uwsgi-tasks-0.4.4.tar.gz", "has_sig": false, "md5_digest": "7bfe858414f14a1ebbca9d05480f7404", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 14585, "upload_time": "2015-05-05T07:52:20", "url": "https://files.pythonhosted.org/packages/45/8e/6c0245b03c07811c7ba6dda09293921620858d721a2fe2fd8b3ffc9105db/uwsgi-tasks-0.4.4.tar.gz" } ], "0.5": [ { "comment_text": "", "digests": { "md5": "609398523c1af223da82ed7807455414", "sha256": "c224f40c07f0d56a770feb0e1b68783553b18c92d671aa2d235582eb552d99f1" }, "downloads": -1, "filename": "uwsgi-tasks-0.5.tar.gz", "has_sig": false, "md5_digest": "609398523c1af223da82ed7807455414", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 17044, "upload_time": "2015-05-30T14:48:17", "url": "https://files.pythonhosted.org/packages/a7/65/1ace385df2cb28db3b4bd090ddd49bae4351adb9a6738f30742256bbced9/uwsgi-tasks-0.5.tar.gz" } ], "0.6": [ { "comment_text": "", "digests": { "md5": "30570d1055e1743d25eb34637c2723d2", "sha256": "8e1b1b64c64f13d39ca419480cc01c590fc103a936644a818bc8096c199a4dd7" }, "downloads": -1, "filename": "uwsgi-tasks-0.6.tar.gz", "has_sig": false, "md5_digest": "30570d1055e1743d25eb34637c2723d2", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 17594, "upload_time": "2015-07-28T20:33:42", "url": "https://files.pythonhosted.org/packages/fd/c8/f2cb4a45382762e81c7634954ed77f647dcb91c5f4c385a2fe0b45fd8df2/uwsgi-tasks-0.6.tar.gz" } ], "0.6.1": [ { "comment_text": "", "digests": { "md5": "9882be77052314601aee3e3c0316e38c", "sha256": "592d75081e49ffa3f8cbdabb0e7a80b64a22f646749400aeeddfb7265bc727fb" }, "downloads": -1, "filename": "uwsgi-tasks-0.6.1.tar.gz", "has_sig": false, "md5_digest": "9882be77052314601aee3e3c0316e38c", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 18913, "upload_time": "2015-07-30T21:20:34", "url": "https://files.pythonhosted.org/packages/9c/17/96e7b1494e60c94562a01dc2201f5ce54da0e3ac5e595c87c80ed53ebab2/uwsgi-tasks-0.6.1.tar.gz" } ], "0.6.3": [ { "comment_text": "", "digests": { "md5": "8ec2b2b2e2bad413447318a735a92d6b", "sha256": "e14b1c3dbd0c56ec7357de07ccfbab6ee3e585d64748846bb5e8a13f7bc305ab" }, "downloads": -1, "filename": "uwsgi-tasks-0.6.3.tar.gz", "has_sig": false, "md5_digest": "8ec2b2b2e2bad413447318a735a92d6b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 19324, "upload_time": "2015-08-03T20:54:31", "url": "https://files.pythonhosted.org/packages/3a/2e/2857374f7b33f2dbc9ccf7e48c813533d36e5e5bb0a1861f58b337a78742/uwsgi-tasks-0.6.3.tar.gz" } ], "0.6.4": [ { "comment_text": "", "digests": { "md5": "e4dfe51e85aed01533c6ffaeb8e91fa7", "sha256": "225a8371f9eaf5a78cc9ef73eff98e7411d493f22c073bb79c42aa54ed7ce09b" }, "downloads": -1, "filename": "uwsgi-tasks-0.6.4.tar.gz", "has_sig": false, "md5_digest": "e4dfe51e85aed01533c6ffaeb8e91fa7", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 19365, "upload_time": "2015-08-03T21:26:23", "url": "https://files.pythonhosted.org/packages/cf/88/8714b0b37084d69c13709c79587e4ba26f372c2885bc95964f2cc2e9f574/uwsgi-tasks-0.6.4.tar.gz" } ], "0.7.2": [ { "comment_text": "", "digests": { "md5": "a889c68f6a0ea64194e9365acad1df5c", "sha256": "133a5c2ef4fbb7954bd9726537db5f7d5a23068dbed1539ed704acf4d4ebcf52" }, "downloads": -1, "filename": "uwsgi-tasks-0.7.2.tar.gz", "has_sig": false, "md5_digest": "a889c68f6a0ea64194e9365acad1df5c", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 18622, "upload_time": "2018-07-25T21:20:07", "url": "https://files.pythonhosted.org/packages/5e/da/f453dc4b7ffbb38cc6fced269c08dff6c429553c6dfc48506d57d4048963/uwsgi-tasks-0.7.2.tar.gz" } ], "0.7.3": [ { "comment_text": "", "digests": { "md5": "75135fbb9d36bc3dcdba27134f2c65d1", "sha256": "def0516398e2309872763d418fe4b7df019acd534685db16f1dd764b6fda7aac" }, "downloads": -1, "filename": "uwsgi_tasks-0.7.3-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "75135fbb9d36bc3dcdba27134f2c65d1", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 15779, "upload_time": "2019-06-13T14:21:10", "url": "https://files.pythonhosted.org/packages/f7/2f/1d89390d94c10b75d46ce3d69c756c9b218156ea798ef80f18c46841659e/uwsgi_tasks-0.7.3-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "70f9f7820983236a1dc182910b4cd250", "sha256": "f8a5dadad74f329bc4d3c6c5197c05c0ed9757fb8df1cdcb8ff324be105f0748" }, "downloads": -1, "filename": "uwsgi-tasks-0.7.3.tar.gz", "has_sig": false, "md5_digest": "70f9f7820983236a1dc182910b4cd250", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 18563, "upload_time": "2019-06-13T14:21:13", "url": "https://files.pythonhosted.org/packages/c2/e0/236801412ed6932aec5f90b5a4f756e5cc1aae3440bca515ec121bb1d910/uwsgi-tasks-0.7.3.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "75135fbb9d36bc3dcdba27134f2c65d1", "sha256": "def0516398e2309872763d418fe4b7df019acd534685db16f1dd764b6fda7aac" }, "downloads": -1, "filename": "uwsgi_tasks-0.7.3-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "75135fbb9d36bc3dcdba27134f2c65d1", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 15779, "upload_time": "2019-06-13T14:21:10", "url": "https://files.pythonhosted.org/packages/f7/2f/1d89390d94c10b75d46ce3d69c756c9b218156ea798ef80f18c46841659e/uwsgi_tasks-0.7.3-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "70f9f7820983236a1dc182910b4cd250", "sha256": "f8a5dadad74f329bc4d3c6c5197c05c0ed9757fb8df1cdcb8ff324be105f0748" }, "downloads": -1, "filename": "uwsgi-tasks-0.7.3.tar.gz", "has_sig": false, "md5_digest": "70f9f7820983236a1dc182910b4cd250", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 18563, "upload_time": "2019-06-13T14:21:13", "url": "https://files.pythonhosted.org/packages/c2/e0/236801412ed6932aec5f90b5a4f756e5cc1aae3440bca515ec121bb1d910/uwsgi-tasks-0.7.3.tar.gz" } ] }