# -*- coding: utf-8 -*-
from setuptools import setup

package_dir = \
{'': 'src'}

packages = \
['aqueue']

package_data = \
{'': ['*']}

install_requires = \
['attrs>=22.1.0,<23.0.0', 'rich>=12.6.0,<13.0.0', 'trio>=0.22.0,<0.23.0']

setup_kwargs = {
    'name': 'aqueue',
    'version': '0.2.0',
    'description': 'An async queue with live progress display',
    'long_description': '# aqueue\n\n![demo](docs/demo.gif)\n\nAn async queue with live progress display. Good for running and visualizing tree-like I/O-bound\nprocessing jobs, such as website scrapes.\n\n## Example\n\n```python\nimport random\n\nimport trio\n\nfrom aqueue import EnqueueFn, Display, run_queue, Item\n\n\nclass RootItem(Item):\n    async def process(self, enqueue: EnqueueFn, display: Display) -> None:\n        num_children = 3\n        display.overall.total = num_children\n        display.worker.description = "Making child items"\n\n        for _ in range(num_children):\n            # simulate doing work and creating more items\n            await trio.sleep(random.random())\n            enqueue(ChildItem())\n\n\nclass ChildItem(Item):\n    async def process(self, enqueue: EnqueueFn, display: Display) -> None:\n        display.worker.description = "Doing work..."\n\n        # Simulate doing work\n        await trio.sleep(random.random())\n\n        display.overall.completed += 1\n\n\ndef main() -> None:\n    run_queue(\n        initial_items=[RootItem()],\n        num_workers=2,\n    )\n\n\nif __name__ == "__main__":\n    main()\n\n```\n\n## Usage Notes\n\nThere\'s two things you need to do to use aqueue:\n\n1. Write your [Item](#items) classes\n2. [Start your queue](#starting-your-queue) with one of those items\n\n### Items\n\nItems are your units of work. They can represent whatever you\'d like, such as parts of a website\nthat you\'re trying to scrape: an item for the index page, for subpages, for images, etc.\n\nEach item should be an instance of a class that defines an async `progress` method. As arguments, it\nshould accept two positional arguments:\n\n1. a `aqueue.EnqueueFn` that caan be called to enqueue more work. That type is simply an alias for\n   `Callable[[Item], None]`.\n2. a `aqueue.Display` object that gives you control of the terminal display:\n\n```python\nimport aqueue\n\nclass MyItem(aqueue.Item):\n    async def process(self, enqueue: aqueue.EnqueueFn, display: aqueue.Display) -> None:\n        # make an HTTP request, parse it, etc\n        print(\'My item is processing!\')\n\n        # when you discover more items you want to process, enqueue them:\n        enqueue(AnotherItem())\n\nclass AnotherItem(aqueue.Item):\n    async def process(self, enqueue: aqueue.EnqueueFn, display: aqueue.Display) -> None:\n        print(\'Another item is processing!\')\n```\n\nAs a rule of thumb, you should make a new item class whenever you notice a one-to-many relationship.\nFor example, this _one_ page has _many_ images I want to download.\n\nNote: `process` is async, but because this library uses\n[Trio](https://trio.readthedocs.io/en/stable/index.html) under the hood, you may only use\nTrio-compatible primitives inside `process`. For example, use `trio.sleep`, not `asyncio.sleep`.\nTODO: consider [AnyIO](https://anyio.readthedocs.io/en/stable/) to avoid this problem?\n\nDisclaimer: aqueue, or any asynchronous framework, is only going to be helpful if you\'re performing\nwork is I/O-bound.\n\n### Starting your Queue\n\nThen, start your queue with an initial item(s) to kick things off.\n\n```python\naqueue.run_queue(\n    initial_items=[MyItem()],\n    num_workers=2,\n)\n```\n\n#### Queue type\n\nBy default, the queue is actually ...a queue -- that is to say that items are processed\nfirst-in-first-out. Here are all the types you can specify with the `queue_type_name` argument.\n\n- `queue` - first-in-first-out processing, or breadth-first.\n- `stack` - last-in-first-out processing, or depth-first. This one is recommended for website\n  scraping because it yields items fast (versus `queue` that tries to look at all the intermediate\n  pages first).\n- `priority` - priority queue processing. In this case, your item objects should be orderable (with\n  `__lt__`, etc). **Lesser objects will be processed first**, because this code uses a minheap.\n\n#### Number of workers\n\nYou can specify the number of workers you\'d like to be processing your items with the `num_workers`\nargument.\n\n#### Ctrl-C\n\nIf you decide you want to stop your queue processing, press Ctrl-C.\n\nIf you\'ve set the `graceful_ctrl_c` to False, this will stop the program immediately. If True, the\ndefault, aqueue will wait for the items currently being worked on to complete (without taking any\nadditional items), and _then_ stop. Put another way, the choice is between responsiveness and\nresource consistency.\n\n#### Setting the look of the panels\n\nCurrently, only support for configuring the "Overall Progress" panel is supported. By default, the\npanel is very simple. If you want to customize it, provide an iterable of\n`rich.progress.ProgressColumn` objects to the `overall_progress_columns` argument. See\n<https://rich.readthedocs.io/en/stable/progress.html> for more information. (Note that rich provides\nall the nice terminal visualizations for aqueue!)\n\n### Updating the display\n\nAs mentioned, each `process` method gets called with an `aqueue.Display` object. The display has two\nproperties:\n\n- `worker`, which lets you update the description of the worker who\'s currently processing this\n  item. `display.worker.description` is the getter/setter for that.\n- `overall`, which lets you access things in "Overall Progress" terminal panel.\n  `display.overall.completed` is a getter/setter for the number of completed things,\n  `display.overall.total` for the total number of things (or None), and `display.overall.total_f`\n  for the total number of things or 0.\n\nThese panels are just an informational display for humans. They don\'t know about the queue churning\nthrough items of work. Therefore, you must decide what things you want to keep track of, and often,\nyou won\'t be able to determine the complete number of things at the beginning. You\'ll need to do\nsome intermediate processing and increment it slowly as more work is discovered. For example, if you\nwant to keep track of images found and downloaded, you often won\'t be able to do that until you are\nsearching deeper into the website.\n\n### Sharing state\n\nOften, its beneficial to share state between the items. Using the website scrape example again, you\nmay want to keep track of the URLs you\'ve visited so you don\'t scrape them twice.\n\nIf this is needed, simply keep a global set/dict/list and store a key for the item. For example, a\nURL string may be a good key.\n\nIf you don\'t want to or can\'t use a global variable, consider a\n[`ContextVar`](https://docs.python.org/3/library/contextvars.html).\n\n### Persisting state\n\nDuring development, its probably likely that your program will crash after doing some work. For\nexample, maybe your HTTP request timed out or you had a bug in your HTML parsing.\n\nIt\'s a shame to lose that work that\'s been done. So, if you\'re looking for a really handy way to\npersist state across runs, check out the built-in\n[`shelve`](https://docs.python.org/3/library/shelve.html) module. It\'s like a dict that\nautomatically saves to a file each time you set a key in it.\n\n### Other cool things\n\nThis library is fully docstringed and type-hinted 🥳\n\n## Installation\n\n```shell\npip install "git+https://github.com/t-mart/aqueue"\n```\n',
    'author': 'Tim Martin',
    'author_email': 'tim@timmart.in',
    'maintainer': 'None',
    'maintainer_email': 'None',
    'url': 'None',
    'package_dir': package_dir,
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'python_requires': '>=3.10,<4.0',
}


setup(**setup_kwargs)
