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

modules = \
['defbench']
install_requires = \
['memory-profiler>=0.58.0,<0.59.0']

setup_kwargs = {
    'name': 'defbench',
    'version': '0.1.3',
    'description': 'benchmark (time + memory) individual python functions',
    'long_description': 'defbench.py\n===\n\na simple python3 module for easily benchmarking the elapsed time and memory usage of python functions\n\n\n## quick start\n```sh\npip install defbench\n```\n\n1. import defbench\n2. define a function to test\n3. use `defbench.run(func[, repeat][, name])`\n\n```python\nimport defbench\n\ndef search_list():\n  "two" in ["one", "two", "three"]\n\nresults = defbench.run(search_list, repeat=1000)\nprint(results)\n```\n*outputs*\n```\n<TestRun \'search_list\'\n  runs:     1,000\n  avg time: 0.0002124s\n  avg mem:  0.0Mib>\n```\n\n## defbench.run()\nthis is the easiest way to use this tool. it generates a `TestRun` object, returns it, and adds it to `defbench.history` for later review or analysis.\n\n```python\nrun(func: Callable, repeat: int = 10, name: str = None) -> TestRun\n```\npass in the function to be tested. optionally, specify the number of times to repeat the function and the name to use. if a name is not provided, it defaults to the function name ("&lt;lambda>" for lambda functions)\n\n## TestRun\na `TestRun` object is returned by either `defbench.run()` or `defbench.Test.run()`. it contains all of the results from benchmarking a function.\n\n### attributes\n| attribute | type | description |\n| --------- | ---- | ----------- |\n| _mem_raw  | List[float] | raw slices of the *entire* program\'s memory usage (MiB) over the course of every repeated call as returned by *memory-profiler* |\n| _mem      | List[float] | _mem_raw "normalized" (initial program memory usage subtracted from all memory usage values) |\n| func      | Callable    | the benchmarked function | \n| name      | str         | the function name *or* the name passed with `.run(func, name="foobar")` |\n| time      | float       | the total time (seconds) of all runs |\n| avg_time  | float       | the average time (seconds) of each run |\n| memory    | float       | the peak memory usage (MiB) |\n| stdout    | str         | output is suppressed by `.run()` and stored here for later retrieval |\n| repeat    | int         | number of tests run |\n\n### initialization\n```python\n__init__(self,\n    func: Callable,\n    name: str = None,\n    repeat: int = 1,\n    mem: List[float] = [],\n    time: float = 0.0,\n    stdout: str = ""\n)\n```\n\na `TestRun` object is *not* meant to be initialized on its own! it is meant to be generated by either `defbench.Test.run()` or `defbench.run()`\n\n### measuring memory\ni want to explain how memory measurement with *memory-profiler* (and thus defbench) works since it took me a minute to understand. essentially, it captures the *entire* program\'s memory usage at specified intervals (0.01s for now, although i might tweak that and/or let the interval be set). when profiling a specific function, it still grabs the *entire* program\'s memory usage; it just does so for the duration that the function is running. i.e.:\n\n1. your python script starts up -- 1MiB -- 1MiB total\n2. you create some lists and do some stuff -- 3MiB -- 4MiB total\n3. you use `defbench.run(my_function)` -- 2MiB -- 6MiB total\n\nso when `defbench.run(my_function)` is called, *memory-profiler* will report **4.0** as the initial memory usage slice and **6.0** as the peak memory slice (e.g.: `[4.0, 4.3, 4.9, 5.5, 6.0]`). this is what\'s stored in `TestRun._mem_raw`. however, we don\'t really care about the rest of the program, so we subtract the initial value from all subsequent memory usage slices (e.g.: `[0.0, 0.3, 0.9, 1.5, 2.0]`). this is what\'s stored in `TestRun._mem`. but since all most users *really* care about is the peak usage, that\'s what\'s returned by `TestRun.memory` (**2.0** in our example).\n\nand just to be totally clear, `max()` is used to find the peak memory usage slice. so even if some objects get released from memory throughout your function, and the last memory usage slice is lower than the peak (e.g.: `[0.0, 0.5, 1.0, 0.8]`), the maximum value is still returned by `TestRun.memory` (e.g.: **1.0**).\n\n### stdout\nduring a `.run()` call, all output to `sys.stdout` (e.g. `print()` statements) is temporarily redirected so that output can be captured. you can access it later using `TestRun.stdout`\n\n## Test\n\na `Test` object is initialiezd with a function name and some default values. calling `Test.run()` will add a new `TestRun` object to `Test.history` and `defbench.history` and then return it.\n\n### attributes\n| attribute | type | description |\n| --------- | ---- | ----------- |\n| _func     | Callable      | the benchmarked function |\n| _repeat   | int           | the default number of times to run the function |\n| _running  | bool          | boolean representing if this `Test` is currently running |\n| name      | str           | the default name to use for tests |\n| history   | List[TestRun] | all `TestRun`s generated by `Test.run()` |\n\n### methods\n```python\nrun(repeat: int = 100, name: str = None) -> TestRun\n```\nreturns a new `TestRun` and appends it to `Test.history`. optionally, set the number of times to run repeat the test and the name to use for this `TestRun`.\n\n### default values priority\npriority for name/repeat values used are:\n\n1. value passed in to `Test.run()` or `defbench.run()`\n2. value passed to `__init__()` (or `Test()`) during initialization\n3. (for name only) function name if neither above provided\n4. default value (10 for repeat and "&lt;function>" for function names, but i\'m not sure a function can even *not* have a name 99% of the time... still, it\'s there just in case someone does some weird code compiling voodoo)\n\n## history\nthere\'s a history object at the module level (`defbench.history`) that contains a history of every `TestRun` object (each instance is added in `TestRun.__init__`).\n\n### attributes\n| attribute | type | description |\n| --------- | ---- | ----------- |\n| _history  | List[TestRun] | all `TestRun` objects |\n\n### methods\n```python\naverage_time(filter: Callable = None) -> float\n```\nreturn the average time of all `TestRun`s. optionally, pass a function to be used as a filter, e.g.: `history.average_time(lambda x: x.name == "str_list_test")`\n\n```python\naverage_memory(filter: Callable = None) -> float\n```\nget the average memory usage in *MiB* of all `TestRun`s. optionally, pass a function to be used as a filter, e.g.: `history.average_memory(lambda x: x.stdout.contains("hello world")`\n\n```python\nadd(run: TestRun) -> None\n```\nadd a `TestRun` to the module level history\n\n```python\nget(filter: Callable = None) -> List[TestRun]\n```\nreturn all `TestRun` objects in history. optionally, pass a function to be used as a filter, e.g.: `history.get(lambda x: x.time > 30)`\n\n# todo\n- [ ] create a generic `_history` class to be used at the module level and by instances of `Test`\n- [ ] add more analysis options for items in `history`\n- [ ] capture `stderr` in `TestRun.run()`?\n- [x] add to pip\n- [ ] add unit tests\n',
    'author': 'Shaun Mitchell',
    'author_email': 'shaun@shitchell.com',
    'maintainer': None,
    'maintainer_email': None,
    'url': 'https://github.com/shitchell/defbench',
    'py_modules': modules,
    'install_requires': install_requires,
    'python_requires': '>=3.4,<4.0',
}


setup(**setup_kwargs)
