{ "info": { "author": "Steve Pulec", "author_email": "spulec@gmail.com", "bugtrack_url": null, "classifiers": [], "description": "PyQS - Python task-queues for Amazon SQS |Build Status| |Coverage Status|\n=========================================================================\n\n**WARNING: This library is still in beta. It has a stable API and has been deployed in production, but we have not received feedback from a large number of use cases, and it is possible there are unknown bugs.**\n\nPyQS is a simple task manager for SQS. It's goal is to provide a simple\nand reliable `celery `__-compatible\ninterface to working with SQS. It uses ``boto3`` under the hood to\n`authenticate `__\nand talk to SQS.\n\nInstallation\n------------\n\n**PyQS** is available from `PyPI `__ and can\nbe installed in all the usual ways. To install via *CLI*:\n\n.. code:: bash\n\n $ pip install pyqs\n\nOr just add it to your ``requirements.txt``.\n\nUsage\n-----\n\nPyQS uses some very simple semantics to create and read tasks. Most of\nthis comes from SQS having a very simple API.\n\nCreating Tasks\n~~~~~~~~~~~~~~\n\nAdding a task to queue is pretty simple.\n\n.. code:: python\n\n from pyqs import task\n\n @task(queue='email')\n def send_email(subject, message):\n pass\n\n send_email.delay(subject='Hi there')\n\n**NOTE:** This assumes that you have your AWS keys in the appropriate\nenvironment variables, or are using IAM roles. PyQS doesn't do anything\ntoo special to talk to AWS, it only creates the appropriate ``boto``\nconnection.\n\nIf you don't pass a queue, PyQS will use the function path as the queue\nname. For example the following function lives in ``email/tasks.py``.\n\n.. code:: python\n\n @task()\n def send_email(subject):\n pass\n\nThis would show up in the ``email.tasks.send_email`` queue.\n\nYou can also specify the function path if you want to reference a function in a different project:\n\n.. code:: python\n\n @task(custom_function_path=\"foo.bar.send_email\")\n # This references function send_email in foo/bar.py instead of email/tasks.py\n def send_email(subject):\n pass\n\n\nReading Tasks\n~~~~~~~~~~~~~\n\nTo read tasks we need to run PyQS. If the task is already in your\n``PYTHON_PATH`` to be imported, we can just run:\n\n.. code:: bash\n\n $ pyqs email.tasks.send_email\n\nIf we want want to run all tasks with a certain prefix. This is based on\nPython's `fnmatch `__.\n\n.. code:: bash\n\n $ pyqs email.*\n\nWe can also read from multiple different queues with one call by\ndelimiting with commas:\n\n.. code:: bash\n\n $ pyqs send_email,read_email,write_email\n\nIf you want to run more workers to process tasks, you can up the\nconcurrency. This will spawn additional processes to work through\nmessages.\n\n.. code:: bash\n\n $ pyqs send_email --concurrency 10\n\nSimple Process Worker\n~~~~~~~~~~~~~~~~~~~~~\n\nTo use a simpler version of PyQS that deals with some of the edge cases in the original implementation, pass the ``simple-worker`` flag.\n\n.. code:: bash\n\n $ pyqs send_email --simple-worker\n\nThe Simple Process Worker differs in the following way from the original implementation.\n\n* Does not use an internal queue and removes support for the ``prefetch-multiplier`` flag. This helps simply the mental model required, as messages are not on both the SQS queue and an internal queue.\n* When the ``simple-worker`` flag is passed, the default ``batchsize`` is 1 instead of 10. This is configurable.\n* Does not check the visibility timeout when reading or processing a message from SQS.\n * Allowing the worker to process the message even past its visibility timeout means we solve the problem of never processing a message if ``max_receives=1`` and we incorrectly set a shorter visibility timeout and exceed the visibility timeout. Previously, this message would have ended up in the DLQ, if one was configured, and never actually processed.\n * It increases the probability that we process a message more than once, especially if ``batchsize > 1``, but this can be solved by the developer checking if the message has already been processed.\n\nHooks\n~~~~~\n\nPyQS has an event registry which can be used to run a function before or after every tasks runs.\n\n.. code:: python\n\n from pyqs import task, events\n\n def print_pre_process(context):\n print({\"pre_process\": context})\n\n def print_post_process(context):\n print({\"pre_process\": context})\n\n events.register_event(\"pre_process\", print_pre_process)\n events.register_event(\"post_process\", print_post_process)\n\n @task(queue=\"my_queue\")\n def send_email(subject):\n pass\n\nOperational Notes\n~~~~~~~~~~~~~~~~~\n\n**Dead Letter Queues**\n\nIt is recommended to use a `Dead Letter Queue `__\nfor any queues that are managed by PyQS. This is because the current strategy\nfor fetching messages does not delete them upon initial receipt. A message is\n**ONLY** deleted from SQS upon successful completion. **This is probably\nunexpected behavior if you are coming from Celery with SQS.** Celery attempted\nto manage this behavior internally, with varying success.\n\nIf an error arises during message processing, it will be discarded and will\nre-appear after the visibility timeout. This can lead to behavior where\nthere are messages that will never leave the queue and continuously throw\nerrors. A Dead Letter Queue helps resolve this by collecting messages that\nhave be retried a specified number of times.\n\n**Worker Seppuku**\n\nEach process worker will shut itself down after ``100`` tasks have been\nprocessed (or failed to process). This is to prevent issues with stale\nconnections lingering and blocking tasks forever. In addition it helps\nguard against memory leaks, though in a rather brutish fashion. After\nthe process worker shut itself down the managing process should notice\nand restart it promptly. The value of ``100`` is currently hard-coded,\nbut could be configurable.\n\n**Queue Blocking**\n\nWhile there are multiple workers for reading from different queues, they\nall append to the same internal queue. This means that if you have one\nqueue with lots of fast tasks, and another with a few slow tasks, they\ncan block eachother and the fast tasks can build up behind the slow\ntasks. The simplest solution is to just run two different ``PyQS``\ncommands, one for each queue with appropriate concurrency settings.\n\n**Visibility Timeout**\n\nCare is taken to not process messages that have exceeded the visibility\ntimeout of their queue. The goal is to prevent double processing of\ntasks. However, it is still quite possible for this to happen since we\ndo not use transactional semantics around tasks. Therefore, it is\nimportant to properly set the visibility timeout on your queues based on\nthe expected length of your tasks. If the timeout is too short, tasks\nwill be processed twice, or very slowly. If it is too long, ephemeral\nfailures will delay messages and reduce the queue throughput\ndrastically. This is related to the queue blocking described above as\nwell. SQS queues are free, so it is good practice to keep the messages\nstored in each as homogenous as possible.\n\nCompatibility\n~~~~~~~~~~~~~\n\n**Celery:**\n\nPyQS was created to replace celery inside of our infrastructure. To\nachieve this goal we wanted to make sure we were compatible with the\nbasic Celery APIs. To this end, you can easily start trying out PyQS in\nyour Celery-based system. PyQS can read messages that Celery has written\nto SQS. It will read ``pickle`` and ``json`` serialized SQS messages\n(Although we recommend JSON).\n\n**Operating Systems:**\n\nUNIX. Due to the use of the ``os.getppid`` system call. This feature can\nprobably be worked around if anyone actually wants windows support.\n\n**Boto3:**\n\nCurrently PyQS only supports a few basic connection parameters being\nexplicitly passed to the connection. Any work ``boto3`` does to\ntransparently find connection credentials, such as IAM roles, will still\nwork properly.\n\nWhen running PyQS from the command-line you can pass ``--region``,\n``--access-key-id``, and ``--secret-access-key`` to override the default\nvalues.\n\nCaveats\n~~~~~~~\n\n**Durability:**\n\nWhen we read a batch of messages from SQS we attempt to add them to our\ninternal queue until we exceed the visibility timeout of the queue. Once\nthis is exceeded, we discard the messages and grab a new batch.\nAdditionally, when a process worker gets a message from the internal\nqueue, the time the message was fetched from SQS is checked against the\nqueues visibility timeout and discarded if it exceeds the timeout. The\ngoal is to reduce double processing. However, this system does not\nprovide transactions and there are cases where it is possible to process\na message whos' visibility timeout has been exceeded. It is up to you to\nmake sure that you can handle this edge case.\n\n**Task Importing:**\n\nCurrently there is not advanced logic in place to find the location of\nmodules to import tasks for processing. PyQS will try using\n``importlib`` to get the module, and then find the task inside the\nmodule. Currently we wrap our usage of PyQS inside a Django admin\ncommand, which simplifies task importing. We call the\n`\\*\\*\\_main()\\*\\* `__\nmethod directly, skipping **main()** since it only performs argument\nparsing.\n\n**Running inside of containers**\n\nPyQS assumes that the process id is not 1. If you are running PyQS inside of a\ncontainer, you should wrap it in supervisor or something like `dummy-init `__.\n\n**Why not just use Celery?**\n\nWe like Celery. We `(Yipit.com) `__ even\nsponsored the `original SQS\nimplementation `__.\nHowever, SQS is pretty different from the rest of the backends that\nCelery supports. Additionally the Celery team does not have the\nresources to create a robust SQS implementation in addition to the rest\nof their duties. This means the SQS is carrying around a lot extra\nfeatures and a complex codebase that makes it hard to debug.\n\nWe have personally experienced some very vexing resource leaks with\nCelery that have been hard to trackdown. For our use case, it has been\nsimpler to switch to a simple library that we fully understand. As this\nlibrary evolves that may change and the the costs of switching may not\nbe worth it. However, we want to provide the option to others who use\npython and SQS to use a simpler setup.\n\n.. |Build Status| image:: https://travis-ci.org/spulec/PyQS.svg?branch=master\n :target: https://travis-ci.org/spulec/PyQS\n.. |Coverage Status| image:: https://coveralls.io/repos/spulec/PyQS/badge.svg?branch=master&service=github\n :target: https://coveralls.io/github/spulec/PyQS?branch=master\n\n\nChangelog\n---------\n\n1.0.1\n~~~~~\n- Add MessageId for tracking task executions\n\n1.0.0\n~~~~~\n- Drop Py2 support\n- Add new SimpleProcessWorker (https://github.com/spulec/PyQS/pull/76)\n\n0.1.6\n~~~~~\n\n- Fix broken pickle of botocore clients.\n\n0.1.5\n~~~~~\n\n- Add events hooks for pre and post processors.\n\n0.1.4\n~~~~~\n\n- Improve behavior when a queue is created after a worker has started. The worker will now refresh the queues every 30 seconds to check for new queues.\n\n0.1.3\n~~~~~\n\n- Change PPID checking to check for actual parent ID, instead of `PID 1`. This fixes issues running on docker containers where PPID of 1 is expected.\n\n0.1.2\n~~~~~\n\n- 419ce2e Merge pull request #56 from orangain/honor-aws-region\n- 7c793d0 Merge pull request #55 from orangain/fix-indentation-error\n- 0643fbb Honor aws region configured by .aws/config or env var\n- f5c1db9 Fix indentation error\n- cdae257 Merge pull request #52 from cedarai/master\n- a2ac378 Merge pull request #53 from p1c2u/fix/nosetest-remove-stop-parameter\n- dbaa391 Merge pull request #51 from p1c2u/fix/pep8-styles-fixes\n- 1577382 Nosetest remove stop parameter\n- b7420e3 Add current directory to PYTHONPATH\n- 8d04b62 Graceful shutdown logging msg fix\n- 796acbc PEP8 styles fixes\n- 72dcb62 Merge pull request #50 from hobbsh/add_example\n- d00d31f Update readme\n- dfbf459 Use .delay() to submit messages\n- 612158f Merge pull request #49 from hobbsh/no_log_0_msg\n- 09a649f Use logger.debug for success SQS log line\n- dfd56c3 Fix typos in readme\n- a774155 Add example flask app\n- 17e7b7c Don't log message retrieve success when there are 0 messages\n- 14eb827 Add shutdown signal logging.\n\n0.1.1\n~~~~~\n\n- Fix KeyError on accounts without queues\n\n0.1.0\n~~~~~\n\n- Upgrade to boto3\n\n0.0.22\n~~~~~~\n\n- Fix Python 3 support\n- Allow overwriting the `delay_seconds` attibute at call time\n\n0.0.21\n~~~~~~\n\n- Add ability to tune ``PREFETCH_MULTIPLIER`` with ``--prefetch-multiplier``.\n\n0.0.20\n~~~~~~\n\n- Respect ``--batch-size`` when sizing internal queue on ManagerWorker\n\n0.0.19\n~~~~~~\n\n- Add ability to run with tunable BATCHSIZE and INTERVAL. Thanks to @masayang\n- Add ability to create tasks with a visibility delay. Thanks to @joshbuddy\n- Add ability to create tasks with a custom function location, allowing cross project tasks\n\n0.0.18\n~~~~~~\n\n- Convert Changelog to .rst\n- Add Changelog to long description on Pypi. Thanks to @adamchainz\n\n0.0.17\n~~~~~~\n\n- Fix typos in README\n- Add notes on Dead Letter Queues to README\n\n0.0.16\n~~~~~~\n\n- Switch README to reStructuredText (.rst) format so it renders on PyPI\n\n0.0.15\n~~~~~~\n\n- Process workers will kill themselves after attempting to process 100\n requests, instead of checking the internal queue 100 times.\n- If we find no messages on the internal queue, sleep for a moment\n before rechecking.\n\n0.0.14\n~~~~~~\n\n- Process workers will kill themselves after processing 100 requests\n- Process workers will check a message's fetch time and visibility\n timeout before processing, discarding it if it has exceeded the\n timeout.\n- Log the ``process_time()`` used to process a task to the INFO level.\n\n0.0.13\n~~~~~~\n\n- Only pass SQS Queue ID to internal queue. This is attempting to fix a\n bug when processing messages from multiple queues.\n\n0.0.12\n~~~~~~\n\n- Remove extraneous debugging code\n\n0.0.11\n~~~~~~\n\n- Add additional debugging to investigate message deletion errors\n\n0.0.10\n~~~~~~\n\n- Give each process worker its own boto connection to avoid\n multiprocess race conditions during message deletion\n\n0.0.9\n-----\n\n- Change long polling interval to a valid value, 0<=LPI<=20\n\n0.0.8\n-----\n\n- Switched to long polling when pulling down messages from SQS.\n- Moved message deletion from SQS until after message has been\n processed.\n\n0.0.7\n-----\n\n- Added capability to read JSON encoded celery messages.\n\n0.0.6\n-----\n\n- Switched shutdown logging to INFO\n- Added brief sleep to message retrieval loop so that we don't look\n like we are using a ton of CPU spinning.\n\n0.0.5\n-----\n\n- Switching task failure logging to ERROR (actually this time)\n- Moved task success logging to INFO\n- Added INFO level logging for number of messages retrieved from an SQS\n queue.\n- Moved Reader and Worker process counts to DEBUG\n\n0.0.4\n-----\n\n- Added ability to pass ``region``, ``access_key_id`` and\n ``secret_access_key`` through to Boto when creating connections\n- Switched logging of task failure to the ``ERROR`` logger, from\n ``INFO``.\n\n\n", "description_content_type": "", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/spulec/pyqs", "keywords": "", "license": "", "maintainer": "", "maintainer_email": "", "name": "pyqs", "package_url": "https://pypi.org/project/pyqs/", "platform": "", "project_url": "https://pypi.org/project/pyqs/", "project_urls": { "Homepage": "https://github.com/spulec/pyqs" }, "release_url": "https://pypi.org/project/pyqs/1.0.1/", "requires_dist": [ "boto3 (>=1.7.0)" ], "requires_python": "", "summary": "A simple task-queue for SQS.", "version": "1.0.1", "yanked": false, "yanked_reason": null }, "last_serial": 11660946, "releases": { "0.0.1": [ { "comment_text": "", "digests": { "md5": "4beb413e8815859a926ba6b6bfb9be2e", "sha256": "21edca520755f148e1911ed55f896a4f972940c2213ee30c37afac86fa06571f" }, "downloads": -1, "filename": "pyqs-0.0.1.tar.gz", "has_sig": false, "md5_digest": "4beb413e8815859a926ba6b6bfb9be2e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 5698, "upload_time": "2013-05-18T03:58:37", "upload_time_iso_8601": "2013-05-18T03:58:37.142862Z", "url": "https://files.pythonhosted.org/packages/2f/73/aaeb90498a1b969550ca3ac832fe1b68b17dcba7ebd4b88a655a75cef8f8/pyqs-0.0.1.tar.gz", "yanked": false, "yanked_reason": null } ], "0.0.10": [ { "comment_text": "", "digests": { "md5": "1bd6a8544bda20383993c7cb08892b25", "sha256": "e6b46ef655fd2ba44f817d7b8d3c9f3fbce053159c656f710d8972a66ed6d2f3" }, "downloads": -1, "filename": "pyqs-0.0.10.tar.gz", "has_sig": false, "md5_digest": "1bd6a8544bda20383993c7cb08892b25", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7922, "upload_time": "2015-08-06T21:48:34", "upload_time_iso_8601": "2015-08-06T21:48:34.049206Z", "url": "https://files.pythonhosted.org/packages/1f/07/8cc74c2324dc2c584ded1496d511ce9c968e4f174bcb7303077025631480/pyqs-0.0.10.tar.gz", "yanked": false, "yanked_reason": null } ], "0.0.11": [ { "comment_text": "", "digests": { "md5": "473da91e5f34dd5b69ab42a6476d2dad", "sha256": "782723ff8a5df4cf1ce0aa2c83a399ccb45469eeeadf24fd10e302a957b72829" }, "downloads": -1, "filename": "pyqs-0.0.11.tar.gz", "has_sig": false, "md5_digest": "473da91e5f34dd5b69ab42a6476d2dad", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8030, "upload_time": "2015-08-07T16:07:23", "upload_time_iso_8601": "2015-08-07T16:07:23.486835Z", "url": "https://files.pythonhosted.org/packages/eb/9b/640eaecc1e261a82c0bf7a1a2b4af27098eb8e7214f78e52c3bb15aebccb/pyqs-0.0.11.tar.gz", "yanked": false, "yanked_reason": null } ], "0.0.12": [ { "comment_text": "", "digests": { "md5": "195116cae37bae59f60649acec93953b", "sha256": "3e9768958ba500b9fec09e6aeb713bae21126d4d98a1f5f1d95c145e0531ab8c" }, "downloads": -1, "filename": "pyqs-0.0.12.tar.gz", "has_sig": false, "md5_digest": "195116cae37bae59f60649acec93953b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7924, "upload_time": "2015-08-07T19:56:17", "upload_time_iso_8601": "2015-08-07T19:56:17.589998Z", "url": "https://files.pythonhosted.org/packages/bf/6a/3aeb7b64c7fc370d1dcdcdf89e554c57be4833fc3a1aaa8e3feb372dc4a3/pyqs-0.0.12.tar.gz", "yanked": false, "yanked_reason": null } ], "0.0.13": [ { "comment_text": "", "digests": { "md5": "c5e3d7af87770bb8603c4475421d415e", "sha256": "da0289e16f50a7d58c4373bc51ff72d62010f934c4e601bf106580074a766e4a" }, "downloads": -1, "filename": "pyqs-0.0.13.tar.gz", "has_sig": false, "md5_digest": "c5e3d7af87770bb8603c4475421d415e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7970, "upload_time": "2015-08-10T18:22:12", "upload_time_iso_8601": "2015-08-10T18:22:12.651595Z", "url": "https://files.pythonhosted.org/packages/d9/00/c8540d0c29fdb573a91a2f0b4df293c6ef821e21ffe6b9081dd4e709fc1b/pyqs-0.0.13.tar.gz", "yanked": false, "yanked_reason": null } ], "0.0.14": [ { "comment_text": "", "digests": { "md5": "1e441c442758a81c91edd8b67db101e2", "sha256": "00a23aa106329b3651eac27db4ab6680e631b99ca6d39614b8f6277d3302ddc9" }, "downloads": -1, "filename": "pyqs-0.0.14.tar.gz", "has_sig": false, "md5_digest": "1e441c442758a81c91edd8b67db101e2", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8957, "upload_time": "2016-02-11T20:59:11", "upload_time_iso_8601": "2016-02-11T20:59:11.827764Z", "url": "https://files.pythonhosted.org/packages/72/62/a50fd61315d98231de73688e42d1e1ccf6b6c3a2b32a9d4162a598224900/pyqs-0.0.14.tar.gz", "yanked": false, "yanked_reason": null } ], "0.0.15": [ { "comment_text": "", "digests": { "md5": "087bfb148b93ad6d95461153d6a4e7ff", "sha256": "53c2a1a384bac85a48379245e881c711ed24009318799a5cb73656cc09e501d9" }, "downloads": -1, "filename": "pyqs-0.0.15.tar.gz", "has_sig": false, "md5_digest": "087bfb148b93ad6d95461153d6a4e7ff", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 9039, "upload_time": "2016-02-18T19:08:16", "upload_time_iso_8601": "2016-02-18T19:08:16.263888Z", "url": "https://files.pythonhosted.org/packages/7d/a2/f8b77846c0d38d86b292a9eb56502bc17e0771385fe696baf2e99e08a842/pyqs-0.0.15.tar.gz", "yanked": false, "yanked_reason": null } ], "0.0.16": [ { "comment_text": "", "digests": { "md5": "133a670a23912b237b0898805516696f", "sha256": "ada55ca5d29aa4c06f7322bb303aa7afb056f8fc0ecb70057b6ecf2cdcde8f85" }, "downloads": -1, "filename": "pyqs-0.0.16.tar.gz", "has_sig": false, "md5_digest": "133a670a23912b237b0898805516696f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 9160, "upload_time": "2016-02-18T19:34:03", "upload_time_iso_8601": "2016-02-18T19:34:03.374082Z", "url": "https://files.pythonhosted.org/packages/27/74/3ea9e3bde69f5b8660fad1261b46af3c90873906d41e1b9314629081e4c3/pyqs-0.0.16.tar.gz", "yanked": false, "yanked_reason": null } ], "0.0.17": [ { "comment_text": "", "digests": { "md5": "b9efe93b2c8d740fab0d1b3a351f3761", "sha256": "ff67f2c37c650fd04728d3c26684320670687cef0dccfb227d9b85d9c0b0b1f7" }, "downloads": -1, "filename": "pyqs-0.0.17.tar.gz", "has_sig": false, "md5_digest": "b9efe93b2c8d740fab0d1b3a351f3761", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 9498, "upload_time": "2016-02-23T01:25:17", "upload_time_iso_8601": "2016-02-23T01:25:17.292556Z", "url": "https://files.pythonhosted.org/packages/18/fe/210fa00abeaabbf626f015ad265bfe9f6a0992b23b5daff12fd330eefc0b/pyqs-0.0.17.tar.gz", "yanked": false, "yanked_reason": null } ], "0.0.18": [ { "comment_text": "", "digests": { "md5": "a43fca967ba83fe01532993ea2d56edd", "sha256": "c15fa46c323d151ff22699978f09bf45eae7538766e6402d9c4f644a4db64e54" }, "downloads": -1, "filename": "pyqs-0.0.18.tar.gz", "has_sig": false, "md5_digest": "a43fca967ba83fe01532993ea2d56edd", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 15910, "upload_time": "2016-09-26T16:11:15", "upload_time_iso_8601": "2016-09-26T16:11:15.579415Z", "url": "https://files.pythonhosted.org/packages/b3/1f/719b1874e836ab52371c374561c87f72f9288074c7f80a62a77f49f141d5/pyqs-0.0.18.tar.gz", "yanked": false, "yanked_reason": null } ], "0.0.19": [ { "comment_text": "", "digests": { "md5": "20fd61a7cfcb38fc80b5d0c13ee7a16f", "sha256": "f9a0deac07c1fe5c9e4414bd9846cbc74d9f1ba76742683ff67276dadf205d0d" }, "downloads": -1, "filename": "pyqs-0.0.19.tar.gz", "has_sig": false, "md5_digest": "20fd61a7cfcb38fc80b5d0c13ee7a16f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 16063, "upload_time": "2017-03-31T21:20:50", "upload_time_iso_8601": "2017-03-31T21:20:50.342315Z", "url": "https://files.pythonhosted.org/packages/e1/61/16f16cbd36155dfa9b54fb225dc73f567d351300eddc0bfb81fc8d315ae6/pyqs-0.0.19.tar.gz", "yanked": false, "yanked_reason": null } ], "0.0.2": [ { "comment_text": "", "digests": { "md5": "4f20807ad19e94f846339788bb8349c1", "sha256": "61bc8da097f1137e4bb707969dbcf0f2fab16b745e44e42bcdfbff7cf3195a62" }, "downloads": -1, "filename": "pyqs-0.0.2.tar.gz", "has_sig": false, "md5_digest": "4f20807ad19e94f846339788bb8349c1", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 4837, "upload_time": "2015-08-03T18:42:41", "upload_time_iso_8601": "2015-08-03T18:42:41.814757Z", "url": "https://files.pythonhosted.org/packages/de/75/07c72f7b637b820e062560c3559cc79b06ca6239b2ea6be2df41079b0492/pyqs-0.0.2.tar.gz", "yanked": false, "yanked_reason": null } ], "0.0.20": [ { "comment_text": "", "digests": { "md5": "ac9bc2c50d0dd9566fe6e7884bdf7dcb", "sha256": "3da8360f99ecc3cc8d4f2144147861c6fc9d4f691e06d8d37ee5511ef2f04c3b" }, "downloads": -1, "filename": "pyqs-0.0.20.tar.gz", "has_sig": false, "md5_digest": "ac9bc2c50d0dd9566fe6e7884bdf7dcb", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 16314, "upload_time": "2017-04-05T15:29:49", "upload_time_iso_8601": "2017-04-05T15:29:49.279808Z", "url": "https://files.pythonhosted.org/packages/ef/a8/1801ec80d09fdd35aa8c067870aab63393d57bad50fa9687be4c3ac87e0e/pyqs-0.0.20.tar.gz", "yanked": false, "yanked_reason": null } ], "0.0.21": [ { "comment_text": "", "digests": { "md5": "b57011f054f693d8b9d90e8dfe079a31", "sha256": "87e01b1ae46c8cdf1aa593c698fb5761b5a8ceebabc169d8e3fa42a37de32552" }, "downloads": -1, "filename": "pyqs-0.0.21.tar.gz", "has_sig": false, "md5_digest": "b57011f054f693d8b9d90e8dfe079a31", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 16465, "upload_time": "2017-04-05T15:58:32", "upload_time_iso_8601": "2017-04-05T15:58:32.825968Z", "url": "https://files.pythonhosted.org/packages/19/2d/a39de1d4f82bafc41a74d64157bf7acd83e4cf5d5d45a62c26162fe420bf/pyqs-0.0.21.tar.gz", "yanked": false, "yanked_reason": null } ], "0.0.22": [ { "comment_text": "", "digests": { "md5": "796d3faff57dfc84b7a96680203dac8d", "sha256": "d7aa6c2fb2872d040ca3781c9d6bddf6a613d6cbf14ce0517ef0032f9d1f8a4d" }, "downloads": -1, "filename": "pyqs-0.0.22.tar.gz", "has_sig": false, "md5_digest": "796d3faff57dfc84b7a96680203dac8d", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 16565, "upload_time": "2017-11-04T17:37:04", "upload_time_iso_8601": "2017-11-04T17:37:04.248786Z", "url": "https://files.pythonhosted.org/packages/21/9a/d9957c69f84fb3b7810285de012e7ed305f2a8f48f885db2623d32b64cd0/pyqs-0.0.22.tar.gz", "yanked": false, "yanked_reason": null } ], "0.0.23": [ { "comment_text": "", "digests": { "md5": "c83d435e6e838868e85556c5d534cb46", "sha256": "83763f9d602b6ca38b86d37d2293759652b884c3a8a22df9ff5de7653ac560f1" }, "downloads": -1, "filename": "pyqs-0.0.23.tar.gz", "has_sig": false, "md5_digest": "c83d435e6e838868e85556c5d534cb46", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 16651, "upload_time": "2017-11-30T19:16:29", "upload_time_iso_8601": "2017-11-30T19:16:29.164847Z", "url": "https://files.pythonhosted.org/packages/e5/d8/f77513f3eee4e9b4605429f143dd6ab7089e4db161cf79fc3617da29fd35/pyqs-0.0.23.tar.gz", "yanked": false, "yanked_reason": null } ], "0.0.3": [ { "comment_text": "", "digests": { "md5": "cf0751fea0f1cb936c51d7c6818dcd5b", "sha256": "fab3e481651eec167218350d83055bf4c751de28f1e16c839073e2f7d2a702b0" }, "downloads": -1, "filename": "pyqs-0.0.3.tar.gz", "has_sig": false, "md5_digest": "cf0751fea0f1cb936c51d7c6818dcd5b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 5491, "upload_time": "2015-08-03T18:55:59", "upload_time_iso_8601": "2015-08-03T18:55:59.957958Z", "url": "https://files.pythonhosted.org/packages/b1/18/77335673cc65240f1c712439876ee2f1be73c5839ef0adef1f524ee64cab/pyqs-0.0.3.tar.gz", "yanked": false, "yanked_reason": null } ], "0.0.4": [ { "comment_text": "", "digests": { "md5": "461a68f2d087e69572e4c04673c78805", "sha256": "a869e9c9b04e5cff5f054050c5f4d523238382e91c79bf185bc285c9591f616b" }, "downloads": -1, "filename": "pyqs-0.0.4.tar.gz", "has_sig": false, "md5_digest": "461a68f2d087e69572e4c04673c78805", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7691, "upload_time": "2015-08-04T16:06:22", "upload_time_iso_8601": "2015-08-04T16:06:22.500158Z", "url": "https://files.pythonhosted.org/packages/fc/b8/84fccc3005bdf2d40a5812d5317690a024f231e92fbd80c387ebb5627151/pyqs-0.0.4.tar.gz", "yanked": false, "yanked_reason": null } ], "0.0.5": [ { "comment_text": "", "digests": { "md5": "5476b873543489e84e5582ca6c8db22c", "sha256": "9e75dc1ecb8bd1524899ecaee19ef3a72bcd0cc2beda086e92d127a68b5ff5a1" }, "downloads": -1, "filename": "pyqs-0.0.5.tar.gz", "has_sig": false, "md5_digest": "5476b873543489e84e5582ca6c8db22c", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7872, "upload_time": "2015-08-04T22:12:59", "upload_time_iso_8601": "2015-08-04T22:12:59.516405Z", "url": "https://files.pythonhosted.org/packages/fb/92/6e89901d7dce65a81b3b230a637ef14ced8e6c3ca0bb6bffffa8f8c12cd1/pyqs-0.0.5.tar.gz", "yanked": false, "yanked_reason": null } ], "0.0.6": [ { "comment_text": "", "digests": { "md5": "32255cff234f665644917036cad00cd7", "sha256": "1ce99f90e8b76cc62407aeaab1b70d06f52e5171c15fd72159bb8cdab7fa4715" }, "downloads": -1, "filename": "pyqs-0.0.6.tar.gz", "has_sig": false, "md5_digest": "32255cff234f665644917036cad00cd7", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7907, "upload_time": "2015-08-05T15:50:54", "upload_time_iso_8601": "2015-08-05T15:50:54.723413Z", "url": "https://files.pythonhosted.org/packages/8b/ea/ee337dc62139cc0616e4ac0e35a943aa44749e95b4dbcc4e54cc68d6cd6f/pyqs-0.0.6.tar.gz", "yanked": false, "yanked_reason": null } ], "0.0.7": [ { "comment_text": "", "digests": { "md5": "7c2c3a33aeeefd59dec8bcbd81d00448", "sha256": "fa0576cec52e2fa2c6be4ebe47042efc6ffe717b348feae14e397309e826bfb3" }, "downloads": -1, "filename": "pyqs-0.0.7.tar.gz", "has_sig": false, "md5_digest": "7c2c3a33aeeefd59dec8bcbd81d00448", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7916, "upload_time": "2015-08-05T19:51:01", "upload_time_iso_8601": "2015-08-05T19:51:01.432124Z", "url": "https://files.pythonhosted.org/packages/a4/29/efeba4c90aa9634a80508ddce5761c46381de0af54a05a9d9ca34a218f0f/pyqs-0.0.7.tar.gz", "yanked": false, "yanked_reason": null } ], "0.0.8": [ { "comment_text": "", "digests": { "md5": "7a5690110755de3cb37724d06f8e71a2", "sha256": "f2d5fdc41358ca51e1f931d434404894ea6f85566739a325432097d7382c612f" }, "downloads": -1, "filename": "pyqs-0.0.8.tar.gz", "has_sig": false, "md5_digest": "7a5690110755de3cb37724d06f8e71a2", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7932, "upload_time": "2015-08-06T18:09:10", "upload_time_iso_8601": "2015-08-06T18:09:10.454741Z", "url": "https://files.pythonhosted.org/packages/7e/17/56f8a2ba411d548bfee9a4dd2227dddf2627dcf1391482d9433164321323/pyqs-0.0.8.tar.gz", "yanked": false, "yanked_reason": null } ], "0.0.9": [ { "comment_text": "", "digests": { "md5": "752eabdbe1ac4c76b65380cf2a68b1dc", "sha256": "2d2447023f41ccd4d3448c47fcf63020e2ccbfa515eb612c6a8157236bc40294" }, "downloads": -1, "filename": "pyqs-0.0.9.tar.gz", "has_sig": false, "md5_digest": "752eabdbe1ac4c76b65380cf2a68b1dc", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7931, "upload_time": "2015-08-06T18:13:10", "upload_time_iso_8601": "2015-08-06T18:13:10.581204Z", "url": "https://files.pythonhosted.org/packages/97/f1/150ee63daffacbc4b13e708354ce350b36683cae984ef4a48fc2459e6059/pyqs-0.0.9.tar.gz", "yanked": false, "yanked_reason": null } ], "0.1.0": [ { "comment_text": "", "digests": { "md5": "477294c48158546bacb950cfa07e4669", "sha256": "1b3b5e890c4ee324e18cbc80a7c63c2a786ef41f59fad18f1ee385c5de8c23fa" }, "downloads": -1, "filename": "pyqs-0.1.0.tar.gz", "has_sig": false, "md5_digest": "477294c48158546bacb950cfa07e4669", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 11992, "upload_time": "2018-08-09T22:44:40", "upload_time_iso_8601": "2018-08-09T22:44:40.438011Z", "url": "https://files.pythonhosted.org/packages/52/a5/2c050d5f5c7f51db7e1b44c836743f071fa88984fa77e1debc9bc3f98574/pyqs-0.1.0.tar.gz", "yanked": false, "yanked_reason": null } ], "0.1.1": [ { "comment_text": "", "digests": { "md5": "d109a20f66d42c46abaf4e88a6817530", "sha256": "a82d067199a0ab9bd2f25327107c7fa524402dbb828c7ce537d10017d775b621" }, "downloads": -1, "filename": "pyqs-0.1.1.tar.gz", "has_sig": false, "md5_digest": "d109a20f66d42c46abaf4e88a6817530", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12021, "upload_time": "2018-08-10T00:35:04", "upload_time_iso_8601": "2018-08-10T00:35:04.656899Z", "url": "https://files.pythonhosted.org/packages/20/10/9e10830ba75ef8bc287b6b2951586c97d546c95ed7f490bbf3312760d625/pyqs-0.1.1.tar.gz", "yanked": false, "yanked_reason": null } ], "0.1.2": [ { "comment_text": "", "digests": { "md5": "c72613a2a2cafc66dd2d5d496eb5a985", "sha256": "712076ba46659e4f4347067dd1c87f3271e5a861842df1a33be374428a3b543b" }, "downloads": -1, "filename": "pyqs-0.1.2.tar.gz", "has_sig": false, "md5_digest": "c72613a2a2cafc66dd2d5d496eb5a985", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12236, "upload_time": "2019-04-23T00:07:38", "upload_time_iso_8601": "2019-04-23T00:07:38.688815Z", "url": "https://files.pythonhosted.org/packages/14/67/39cb3c43593ba37335f3f07f2dc4530c7093c0c2a99a1d7278bf96d96bd5/pyqs-0.1.2.tar.gz", "yanked": false, "yanked_reason": null } ], "0.1.3": [ { "comment_text": "", "digests": { "md5": "9e38f21ae815edad1f7838b321e15542", "sha256": "8ae751aaab5ac0cb1df18d9dc3967b5094089fca55235525605b3c3a2bf41b75" }, "downloads": -1, "filename": "pyqs-0.1.3.tar.gz", "has_sig": false, "md5_digest": "9e38f21ae815edad1f7838b321e15542", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 18652, "upload_time": "2019-10-29T20:13:23", "upload_time_iso_8601": "2019-10-29T20:13:23.850705Z", "url": "https://files.pythonhosted.org/packages/95/04/8025a70f58f72765390078e44e8b5d8ee86e3db13e98274b31fd6c2aa132/pyqs-0.1.3.tar.gz", "yanked": false, "yanked_reason": null } ], "0.1.4": [ { "comment_text": "", "digests": { "md5": "11903f49153b039dba21b5e7d36389c0", "sha256": "284a033648749f213c89aa6abc2665d4796fea57a82d5e5e9bc088953b25572d" }, "downloads": -1, "filename": "pyqs-0.1.4.tar.gz", "has_sig": false, "md5_digest": "11903f49153b039dba21b5e7d36389c0", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13213, "upload_time": "2019-11-12T19:20:07", "upload_time_iso_8601": "2019-11-12T19:20:07.748561Z", "url": "https://files.pythonhosted.org/packages/13/96/1cdc09de6dc3c989f7aa013506b3d9064099c0b06d724d8996dc36edbaf2/pyqs-0.1.4.tar.gz", "yanked": false, "yanked_reason": null } ], "0.1.5": [ { "comment_text": "", "digests": { "md5": "98667b9282add2aa940586497828ba83", "sha256": "f16c97f054446fb6a269ff01dd0eeab91b10e7135fd40bb2eef06adbb4abdf3e" }, "downloads": -1, "filename": "pyqs-0.1.5.tar.gz", "has_sig": false, "md5_digest": "98667b9282add2aa940586497828ba83", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20328, "upload_time": "2020-06-25T21:20:10", "upload_time_iso_8601": "2020-06-25T21:20:10.235971Z", "url": "https://files.pythonhosted.org/packages/26/48/02d6b4a60d5de66312a0b92245d10808246e387e60883b3ad404c049bc01/pyqs-0.1.5.tar.gz", "yanked": false, "yanked_reason": null } ], "0.1.6": [ { "comment_text": "", "digests": { "md5": "778821fb64655e415648f609ab1d75b3", "sha256": "8a65d329830683b7e056d919c6f20721bc1efb30e8b755e031306d13cd6db1b6" }, "downloads": -1, "filename": "pyqs-0.1.6-py3-none-any.whl", "has_sig": false, "md5_digest": "778821fb64655e415648f609ab1d75b3", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 14912, "upload_time": "2021-01-18T20:33:00", "upload_time_iso_8601": "2021-01-18T20:33:00.003728Z", "url": "https://files.pythonhosted.org/packages/0b/54/8423484c2d9664c5cbacf7f8386dc9eb9681dc5209cb9962d2f61a4d5b24/pyqs-0.1.6-py3-none-any.whl", "yanked": false, "yanked_reason": null }, { "comment_text": "", "digests": { "md5": "25dd35e013fddb127f6c3b2938e57cdd", "sha256": "7550ce96256286c77bc9feb0f9e25a9d2915a38ec03975a873ff3d884e1cf2c8" }, "downloads": -1, "filename": "pyqs-0.1.6.tar.gz", "has_sig": false, "md5_digest": "25dd35e013fddb127f6c3b2938e57cdd", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20549, "upload_time": "2021-01-18T20:33:01", "upload_time_iso_8601": "2021-01-18T20:33:01.224723Z", "url": "https://files.pythonhosted.org/packages/4d/c3/928d2b5e5f61e84b14c602ff34d03f0e12cf95f44dddb92c61b8ed6a2d84/pyqs-0.1.6.tar.gz", "yanked": false, "yanked_reason": null } ], "1.0.0": [ { "comment_text": "", "digests": { "md5": "9b315f1b26d14a66c461a9d3a017feda", "sha256": "b8a0a72b4b10e2d1215b6946a2f09fe67f001fb9babfb8e2944516a2f90f52dc" }, "downloads": -1, "filename": "pyqs-1.0.0-py3-none-any.whl", "has_sig": false, "md5_digest": "9b315f1b26d14a66c461a9d3a017feda", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 15591, "upload_time": "2021-05-14T20:21:07", "upload_time_iso_8601": "2021-05-14T20:21:07.578604Z", "url": "https://files.pythonhosted.org/packages/89/ca/2d5f30b50f7258228a34829a7171d6d59049ae276ee855035987c1efd215/pyqs-1.0.0-py3-none-any.whl", "yanked": false, "yanked_reason": null }, { "comment_text": "", "digests": { "md5": "5c89befbbac3e141bcab921632061ceb", "sha256": "865d2e4abc53434dfc69ff810372db25f7d8e96d24f5ad0d288b607269d425c8" }, "downloads": -1, "filename": "pyqs-1.0.0.tar.gz", "has_sig": false, "md5_digest": "5c89befbbac3e141bcab921632061ceb", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 21993, "upload_time": "2021-05-14T20:21:09", "upload_time_iso_8601": "2021-05-14T20:21:09.222612Z", "url": "https://files.pythonhosted.org/packages/1c/f1/36ad3fe2bccfe9e35fc3612b6901f5daa1824c58ca585e47196f2b018507/pyqs-1.0.0.tar.gz", "yanked": false, "yanked_reason": null } ], "1.0.1": [ { "comment_text": "", "digests": { "md5": "dbeab5f463e730c3dc298001b143fce3", "sha256": "fdd97625afeb4f675008a41667b926307bf248b19c9a32f566feea0af2ae7600" }, "downloads": -1, "filename": "pyqs-1.0.1-py3-none-any.whl", "has_sig": false, "md5_digest": "dbeab5f463e730c3dc298001b143fce3", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 16080, "upload_time": "2021-10-08T00:29:56", "upload_time_iso_8601": "2021-10-08T00:29:56.882067Z", "url": "https://files.pythonhosted.org/packages/ae/3d/53da6006926985c0c3346f431c4e3ed63d6045c6ad7e6ae12367e5d298cd/pyqs-1.0.1-py3-none-any.whl", "yanked": false, "yanked_reason": null }, { "comment_text": "", "digests": { "md5": "f09c0bdc6406e607c830423bbc93c90a", "sha256": "56a36bfa88020111f6dc58ef485f525d47d08e5d195fc45fc1f54ec24a91bad7" }, "downloads": -1, "filename": "pyqs-1.0.1.tar.gz", "has_sig": false, "md5_digest": "f09c0bdc6406e607c830423bbc93c90a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 21600, "upload_time": "2021-10-08T00:29:58", "upload_time_iso_8601": "2021-10-08T00:29:58.706500Z", "url": "https://files.pythonhosted.org/packages/f2/dc/3199101fb15909a89421cef68d7045f9759e7f0bfcd9a40c0cc3b5f3ec44/pyqs-1.0.1.tar.gz", "yanked": false, "yanked_reason": null } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "dbeab5f463e730c3dc298001b143fce3", "sha256": "fdd97625afeb4f675008a41667b926307bf248b19c9a32f566feea0af2ae7600" }, "downloads": -1, "filename": "pyqs-1.0.1-py3-none-any.whl", "has_sig": false, "md5_digest": "dbeab5f463e730c3dc298001b143fce3", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 16080, "upload_time": "2021-10-08T00:29:56", "upload_time_iso_8601": "2021-10-08T00:29:56.882067Z", "url": "https://files.pythonhosted.org/packages/ae/3d/53da6006926985c0c3346f431c4e3ed63d6045c6ad7e6ae12367e5d298cd/pyqs-1.0.1-py3-none-any.whl", "yanked": false, "yanked_reason": null }, { "comment_text": "", "digests": { "md5": "f09c0bdc6406e607c830423bbc93c90a", "sha256": "56a36bfa88020111f6dc58ef485f525d47d08e5d195fc45fc1f54ec24a91bad7" }, "downloads": -1, "filename": "pyqs-1.0.1.tar.gz", "has_sig": false, "md5_digest": "f09c0bdc6406e607c830423bbc93c90a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 21600, "upload_time": "2021-10-08T00:29:58", "upload_time_iso_8601": "2021-10-08T00:29:58.706500Z", "url": "https://files.pythonhosted.org/packages/f2/dc/3199101fb15909a89421cef68d7045f9759e7f0bfcd9a40c0cc3b5f3ec44/pyqs-1.0.1.tar.gz", "yanked": false, "yanked_reason": null } ], "vulnerabilities": [] }