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

packages = \
['parallely']

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

install_requires = \
['asyncio>=3.4.3,<4.0.0', 'nest-asyncio>=1.5.1,<2.0.0', 'pathos>=0.2.8,<0.3.0']

setup_kwargs = {
    'name': 'parallely',
    'version': '0.2.4',
    'description': 'The simplest way to utilize multiple threads, processes, and async functions in Python',
    'long_description': '# Parallely - Parallel Python made simple\n\n[![pypi](https://img.shields.io/pypi/v/parallely.svg)](https://pypi.org/project/parallely/)\n[![License](https://img.shields.io/github/license/mvilstrup/parallely)](https://github.com/mvilstrup/parallely/blob/main/LICENSE)\n[![wheel](https://img.shields.io/pypi/wheel/parallely.svg)](https://pypi.org/project/parallely/)\n[![python](https://img.shields.io/pypi/pyversions/parallely.svg)](https://pypi.org/project/parallely/)\n[![Test Suite](https://github.com/mvilstrup/parallely/workflows/Test%20Suite/badge.svg)](https://github.com/mvilstrup/parallely/actions?query=workflow%3A%22Test+Suite%22)\n[![Coverage Status](https://coveralls.io/repos/github/MVilstrup/parallely/badge.svg?branch=main)](https://coveralls.io/github/MVilstrup/parallely?branch=main)\n[![docs](https://readthedocs.org/projects/parallely/badge/?version=latest)](https://parallely.readthedocs.io/en/latest/?badge=latest)\n\n## Installation\nTo install this library, all you have to do is write `pip install parallely`. The library is really simple with just 3 functions. However, if needed, you can find the docs at [ReadTheDocs](https://parallely.readthedocs.io/en/latest/index.html)\n\n# Overview\n\nDealing with multi-threading, parallel processes, and concurrent functions can be difficult in Python. A lot of boiler plate code is needed, and it is difficult to gauge whether it actually improved performance or not. \n\nIn some cases, it is necessary to tailor the program to utilize the underlying computational resources. In most cases, we just want to do the same thing many times with small alterations. In these scenarios `parallely` can make your life much easier. \n\n# Multi Threading\nDue to the [GIL](https://wiki.python.org/moin/GlobalInterpreterLock) multi threading is far from as useful in Python as it is in other langauges. However, when dealing with I/O intensive applications it can still be really usefull to have multiple threads waiting for a response in parallel instead of waiting for each response sequentially, if you are confused by this, there are plenty of tutorials etc. to [help you out](https://www.google.com/search?q=why%20multi%20thread%20python)\n\nMost of the time we just want to make a series of webrequests. In this case `parallely` removes all complexity of handling all the threads with a single decorator `threaded`. Which is easiest to explain with an example:\n\n```python\nimport time\nfrom parallely import threaded\n\n@threaded\ndef thread_function(name, duration, other_arg):\n    print(f"Thread {name}: starting", other_arg)\n    time.sleep(duration)\n    print(f"Thread {name}: finishing", other_arg)\n\n\nprint("Synchronous")\nthread_function(1, duration=2, other_arg="hello world")\nthread_function(2, duration=1, other_arg="hello world")\n# NOTICE: We can use the thread_function the exact way we would expect without any overhead\n\nprint()\nprint("Parallel/Asynchrous")\nthread_function.map(name=[1, 2], duration=[2, 1], other_arg="hello world")\n# NOTICE: the constant given to \'other_arg\' is repeated in all function calls\n# thread_function.map([1, 2], [2, 1], "hello world") would produce a similar result\n```\n\nAs can be seen, the decorated function can be used like one would expect, which makes it easiest to test the function etc. However, it allso gets a `.map()` attribute, which can be used to run the function in a parallel manner.\n\n# Multi Processing\nWorking with multiple processes is one of the only ways to get around the GIL. However, this approach has [all sorts of problems on its own](https://www.google.com/search?q=problems%20when%20working%20with%20multi%20processes%20python). In many cases transferring the data between processes takes more time than the actual calculations. However, some times it can dramatically speed things up. `parallely` makes it just as easy to work with multiple processes as it does with threads. Simply use the decorator `parallel` as can be seen in the example below.\n\n```python\nfrom time import time\nfrom random import randint\nfrom parallely import parallel\n\n\n@parallel\ndef count_in_range(size, search_minimum, search_maximum):\n    """Returns how many numbers lie within `maximum` and `minimum` in a random array"""\n    rand_arr = [randint(0, 10) for _ in range(int(size))] \n    return sum([search_minimum <= n <= search_maximum for n in rand_arr])\n\nsize = 1e7\n\nprint("Sequential")\nstart_time = time()\nfor _ in range(4):\n    result = count_in_range(size, search_minimum=1, search_maximum=2)\n    print(result, round(time() - start_time, 2), "seconds")\n\nprint()\n\nprint("Parallel")\nstart_time = time()\nresult = count_in_range.map(size=[size, size, size, size], search_minimum=1, search_maximum=2)\nprint(result, round(time() - start_time, 2), "seconds")\n```\n\n# Asynchronous\n\n```python\nimport asyncio\nimport time\nfrom random import randint\nfrom parallely import asynced\n\n\nasync def echo(delay, start_time):\n    await asyncio.sleep(randint(0, delay))\n    print(delay, round(time.time() - start_time, 1))\n\n@asynced\nasync def main(counts):\n    start_time = time.time()\n    print(f"started at {time.strftime(\'%X\')}")\n    \n    corr = []\n    for count in range(counts):\n        corr.append(echo(count, start_time))\n        \n    await asyncio.gather(*corr)\n\n    print(f"finished at {time.strftime(\'%X\')}")\n\n# The asynchronous function can now be called in a synchronous manner without awiting it\nmain(10)\n\n# It can also be called in a parallel manner\nmain.map([5, 5])\n```',
    'author': 'Mikkel Vilstrup',
    'author_email': 'mikkel@vilstrup.dk',
    'maintainer': None,
    'maintainer_email': None,
    'url': None,
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'python_requires': '>=3.7,<3.11',
}


setup(**setup_kwargs)
