=====================
charmonium.time_block
=====================
A decorator and a context-manager (with-statment) to time a block of
code.
Quickstart
----------
::
$ pip install charmonium.time_block
.. code:: python
>>> import charmonium.time_block as ch_time_block
>>> ch_time_block._enable_doctest_logging()
>>> import time
>>>
>>> def foo():
... with ch_time_block.ctx("bar"):
... time.sleep(0.1)
...
>>> foo()
> bar: running
> bar: 0.1s
Equivalent context-manager:
.. code:: python
>>> import charmonium.time_block as ch_time_block
>>> ch_time_block._enable_doctest_logging()
>>>
>>> def foo():
... bar()
...
>>>
>>> @ch_time_block.decor("bar")
... def bar():
... time.sleep(0.1)
...
>>> foo()
> bar: running
> bar: 0.1s
`line_prof`_ is extremely detailed and complex, which makes it more
appropriate when you don't know what to measure, whereas this package
is more appropriate when you already know the bottleneck, and just
want to see how slow a few functions/blocks are.
.. _`line_prof`: https://github.com/rkern/line_profiler
Unlike external profiling, This does not need source-code access, so
it will work from ``.eggs``.
Unlike external profiling, this package reports in realtime to
`logger`_ (destination customizable). This is intended to let the user
know what the code is doing right now.
.. _`logger`: https://docs.python.org/3.9/library/logging.html
::
> download: running
> download: 0.1s
> processing: running
> processing > decompress: running
> processing > decompress: 0.2s
> processing: 0.4s
Since this plugs into Python's
`logger`_ infrastructure, this can feed a pipeline that checks the
application health (e.g. ensuring a microservice is responsive).
.. _`logger`: https://docs.python.org/3.9/library/logging.html
This records process's increase in memory usage (relatively
cross-platform method using `psutil`_) when ``do_gc=True``, which
gives a rough estimate of the memory leaked by the block.
.. _`psutil`: https://github.com/giampaolo/psutil
Like function profiling, but unlike other block-profilers, it is
recurrent, and it maintains a stack.
.. code:: python
>>> import charmonium.time_block as ch_time_block
>>> ch_time_block._enable_doctest_logging()
>>> import time
>>>
>>> @ch_time_block.decor()
... def foo():
... time.sleep(0.1)
... bar()
...
>>>
>>> @ch_time_block.decor()
... def bar():
... time.sleep(0.2)
... with ch_time_block.ctx("baz"):
... time.sleep(0.3)
...
>>> foo()
> foo: running
> foo > bar: running
> foo > bar > baz: running
> foo > bar > baz: 0.3s
> foo > bar: 0.5s
> foo: 0.6s
This handles recursion.
.. code:: python
>>> import charmonium.time_block as ch_time_block
>>> ch_time_block._enable_doctest_logging()
>>> import time
>>>
>>> @ch_time_block.decor(print_args=True)
... def foo(n):
... if n != 0:
... time.sleep(0.1)
... return foo(n - 1)
...
>>> foo(2)
> foo(2): running
> foo(2) > foo(1): running
> foo(2) > foo(1) > foo(0): running
> foo(2) > foo(1) > foo(0): 0.0s
> foo(2) > foo(1): 0.1s
> foo(2): 0.2s
This even works for threads (or more usefully `ThreadPoolExecutor`_).
.. _`ThreadPoolExecutor`: https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.ThreadPoolExecutor
.. code:: python
>>> import charmonium.time_block as ch_time_block
>>> ch_time_block._enable_doctest_logging()
>>> import time
>>> from concurrent.futures import ThreadPoolExecutor
>>>
>>> @ch_time_block.decor()
... def foo():
... time.sleep(0.1)
... baz()
...
>>> @ch_time_block.decor()
... def bar():
... time.sleep(0.2)
... baz()
...
>>> @ch_time_block.decor()
... def baz():
... return time.sleep(0.3)
...
>>> from threading import Thread
>>> threads = [Thread(target=foo), Thread(target=bar)]
>>> for thread in threads: # doctest:+SKIP
... thread.start()
...
> foo: running
> bar: running
> foo > baz: running
> bar > baz: running
> foo > baz: 0.3s
> foo: 0.4s
> bar > baz: 0.3s
> bar: 0.5s
>>> # TODO: get a better example, with named threads
The results are programatically accessible at runtime. In the dict
returned by ``get_stats()``, the stack frame (key) is represented as a
tuple of strings while the profile result (value) is a pair of time
and memory used.
.. code:: python
>>> import charmonium.time_block as ch_time_block
>>> ch_time_block._enable_doctest_logging()
>>> ch_time_block.clear()
>>> import time
>>>
>>> @ch_time_block.decor()
... def foo():
... time.sleep(0.1)
... bar()
...
>>>
>>> @ch_time_block.decor()
... def bar():
... time.sleep(0.2)
...
>>> [foo() for _ in range(2)]
> foo: running
> foo > bar: running
> foo > bar: 0.2s
> foo: 0.3s
> foo: running
> foo > bar: running
> foo > bar: 0.2s
> foo: 0.3s
[None, None]
>>> ch_time_block.get_stats() # doctest:+SKIP
{('foo', 'bar'): [(0.2, 0), (0.2, 0)], ('foo',): [(0.3, 0), (0.3, 0)]}
>>> ch_time_block.print_stats() # doctest:+SKIP
foo = 100% of total = 100% of parent = (0.3 +/- 0.0) sec = 2*(0.2 +/- 0.0) sec using (0.0 +/- 0.0) B
foo > bar = 100% of total = 66% of parent = (0.2 +/- 0.0) sec = 2*(0.1 +/- 0.0) sec using (0.0 +/- 0.0) B
Raw data
{
"_id": null,
"home_page": "https://github.com/charmoniumQ/charmonium.time_block.git",
"name": "charmonium.time_block",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.7,<4.0",
"maintainer_email": "",
"keywords": "",
"author": "Samuel Grayson",
"author_email": "sam+dev@samgrayson.me",
"download_url": "https://files.pythonhosted.org/packages/ef/01/60a30278274ef69734589ce06e5b6b85e2e89e6ab4b40a7bd53eb52ae071/charmonium_time_block-0.3.4.tar.gz",
"platform": null,
"description": "=====================\ncharmonium.time_block\n=====================\n\nA decorator and a context-manager (with-statment) to time a block of\ncode.\n\n\nQuickstart\n----------\n\n::\n\n $ pip install charmonium.time_block\n\n.. code:: python\n\n >>> import charmonium.time_block as ch_time_block\n >>> ch_time_block._enable_doctest_logging()\n >>> import time\n >>> \n >>> def foo():\n ... with ch_time_block.ctx(\"bar\"):\n ... time.sleep(0.1)\n ... \n >>> foo()\n > bar: running\n > bar: 0.1s\n\nEquivalent context-manager:\n\n.. code:: python\n\n\n >>> import charmonium.time_block as ch_time_block\n >>> ch_time_block._enable_doctest_logging()\n >>> \n >>> def foo():\n ... bar()\n ... \n >>> \n >>> @ch_time_block.decor(\"bar\")\n ... def bar():\n ... time.sleep(0.1)\n ... \n >>> foo()\n > bar: running\n > bar: 0.1s\n\n`line_prof`_ is extremely detailed and complex, which makes it more\nappropriate when you don't know what to measure, whereas this package\nis more appropriate when you already know the bottleneck, and just\nwant to see how slow a few functions/blocks are.\n\n.. _`line_prof`: https://github.com/rkern/line_profiler\n\nUnlike external profiling, This does not need source-code access, so\nit will work from ``.eggs``.\n\nUnlike external profiling, this package reports in realtime to\n`logger`_ (destination customizable). This is intended to let the user\nknow what the code is doing right now.\n\n.. _`logger`: https://docs.python.org/3.9/library/logging.html\n\n::\n\n > download: running\n > download: 0.1s\n > processing: running\n > processing > decompress: running\n > processing > decompress: 0.2s\n > processing: 0.4s\n\nSince this plugs into Python's\n`logger`_ infrastructure, this can feed a pipeline that checks the\napplication health (e.g. ensuring a microservice is responsive).\n\n.. _`logger`: https://docs.python.org/3.9/library/logging.html\n\nThis records process's increase in memory usage (relatively\ncross-platform method using `psutil`_) when ``do_gc=True``, which\ngives a rough estimate of the memory leaked by the block.\n\n.. _`psutil`: https://github.com/giampaolo/psutil\n\nLike function profiling, but unlike other block-profilers, it is\nrecurrent, and it maintains a stack.\n\n.. code:: python\n\n >>> import charmonium.time_block as ch_time_block\n >>> ch_time_block._enable_doctest_logging()\n >>> import time\n >>> \n >>> @ch_time_block.decor()\n ... def foo():\n ... time.sleep(0.1)\n ... bar()\n ... \n >>> \n >>> @ch_time_block.decor()\n ... def bar():\n ... time.sleep(0.2)\n ... with ch_time_block.ctx(\"baz\"):\n ... time.sleep(0.3)\n ... \n >>> foo()\n > foo: running\n > foo > bar: running\n > foo > bar > baz: running\n > foo > bar > baz: 0.3s\n > foo > bar: 0.5s\n > foo: 0.6s\n\nThis handles recursion.\n\n.. code:: python\n\n >>> import charmonium.time_block as ch_time_block\n >>> ch_time_block._enable_doctest_logging()\n >>> import time\n >>> \n >>> @ch_time_block.decor(print_args=True)\n ... def foo(n):\n ... if n != 0:\n ... time.sleep(0.1)\n ... return foo(n - 1)\n ...\n >>> foo(2)\n > foo(2): running\n > foo(2) > foo(1): running\n > foo(2) > foo(1) > foo(0): running\n > foo(2) > foo(1) > foo(0): 0.0s\n > foo(2) > foo(1): 0.1s\n > foo(2): 0.2s\n\nThis even works for threads (or more usefully `ThreadPoolExecutor`_).\n\n.. _`ThreadPoolExecutor`: https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.ThreadPoolExecutor\n\n.. code:: python\n\n >>> import charmonium.time_block as ch_time_block\n >>> ch_time_block._enable_doctest_logging()\n >>> import time\n >>> from concurrent.futures import ThreadPoolExecutor\n >>> \n >>> @ch_time_block.decor()\n ... def foo():\n ... time.sleep(0.1)\n ... baz()\n ... \n >>> @ch_time_block.decor()\n ... def bar():\n ... time.sleep(0.2)\n ... baz()\n ... \n >>> @ch_time_block.decor()\n ... def baz():\n ... return time.sleep(0.3)\n ... \n >>> from threading import Thread\n >>> threads = [Thread(target=foo), Thread(target=bar)]\n >>> for thread in threads: # doctest:+SKIP\n ... thread.start()\n ...\n > foo: running\n > bar: running\n > foo > baz: running\n > bar > baz: running\n > foo > baz: 0.3s\n > foo: 0.4s\n > bar > baz: 0.3s\n > bar: 0.5s\n >>> # TODO: get a better example, with named threads\n\nThe results are programatically accessible at runtime. In the dict\nreturned by ``get_stats()``, the stack frame (key) is represented as a\ntuple of strings while the profile result (value) is a pair of time\nand memory used.\n\n.. code:: python\n\n >>> import charmonium.time_block as ch_time_block\n >>> ch_time_block._enable_doctest_logging()\n >>> ch_time_block.clear()\n >>> import time\n >>> \n >>> @ch_time_block.decor()\n ... def foo():\n ... time.sleep(0.1)\n ... bar()\n ... \n >>> \n >>> @ch_time_block.decor()\n ... def bar():\n ... time.sleep(0.2)\n ... \n >>> [foo() for _ in range(2)]\n > foo: running\n > foo > bar: running\n > foo > bar: 0.2s\n > foo: 0.3s\n > foo: running\n > foo > bar: running\n > foo > bar: 0.2s\n > foo: 0.3s\n [None, None]\n >>> ch_time_block.get_stats() # doctest:+SKIP\n {('foo', 'bar'): [(0.2, 0), (0.2, 0)], ('foo',): [(0.3, 0), (0.3, 0)]}\n >>> ch_time_block.print_stats() # doctest:+SKIP\n foo = 100% of total = 100% of parent = (0.3 +/- 0.0) sec = 2*(0.2 +/- 0.0) sec using (0.0 +/- 0.0) B\n foo > bar = 100% of total = 66% of parent = (0.2 +/- 0.0) sec = 2*(0.1 +/- 0.0) sec using (0.0 +/- 0.0) B\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Time a block of code.",
"version": "0.3.4",
"project_urls": {
"Homepage": "https://github.com/charmoniumQ/charmonium.time_block.git",
"Repository": "https://github.com/charmoniumQ/charmonium.time_block.git"
},
"split_keywords": [],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "77d6d9175057fbd844709091639aad9722a88f0af9c4eef98c538b3d6f00dd44",
"md5": "621575d74e834f040a61368e0f7151d6",
"sha256": "a7275021dec3da6048163c41019d0fbd975528458a3629323eb66730b70fda1f"
},
"downloads": -1,
"filename": "charmonium_time_block-0.3.4-py3-none-any.whl",
"has_sig": false,
"md5_digest": "621575d74e834f040a61368e0f7151d6",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.7,<4.0",
"size": 9094,
"upload_time": "2024-02-15T07:06:55",
"upload_time_iso_8601": "2024-02-15T07:06:55.982017Z",
"url": "https://files.pythonhosted.org/packages/77/d6/d9175057fbd844709091639aad9722a88f0af9c4eef98c538b3d6f00dd44/charmonium_time_block-0.3.4-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "ef0160a30278274ef69734589ce06e5b6b85e2e89e6ab4b40a7bd53eb52ae071",
"md5": "5446eb6a145b23c4185e39de14b16f33",
"sha256": "5cbde16fdfc927393a473297690138c2db169e51cdfff13accbbf6ec44486968"
},
"downloads": -1,
"filename": "charmonium_time_block-0.3.4.tar.gz",
"has_sig": false,
"md5_digest": "5446eb6a145b23c4185e39de14b16f33",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.7,<4.0",
"size": 9076,
"upload_time": "2024-02-15T07:06:57",
"upload_time_iso_8601": "2024-02-15T07:06:57.543997Z",
"url": "https://files.pythonhosted.org/packages/ef/01/60a30278274ef69734589ce06e5b6b85e2e89e6ab4b40a7bd53eb52ae071/charmonium_time_block-0.3.4.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-02-15 07:06:57",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "charmoniumQ",
"github_project": "charmonium.time_block",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "charmonium.time_block"
}