METADATA 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. Metadata-Version: 2.1
  2. Name: about-time
  3. Version: 4.2.1
  4. Summary: Easily measure timing and throughput of code blocks, with beautiful human friendly representations.
  5. Home-page: https://github.com/rsalmei/about-time
  6. Author: Rogério Sampaio de Almeida
  7. Author-email: rsalmei@gmail.com
  8. License: MIT
  9. Keywords: python,track,tracker,time,code,blocks,monitor,statistics,analytics
  10. Platform: UNKNOWN
  11. Classifier: Development Status :: 5 - Production/Stable
  12. Classifier: Intended Audience :: Developers
  13. Classifier: Environment :: Console
  14. Classifier: License :: OSI Approved :: MIT License
  15. Classifier: Programming Language :: Python :: 3
  16. Classifier: Programming Language :: Python :: 3.7
  17. Classifier: Programming Language :: Python :: 3.8
  18. Classifier: Programming Language :: Python :: 3.9
  19. Classifier: Programming Language :: Python :: 3.10
  20. Classifier: Programming Language :: Python :: 3.11
  21. Requires-Python: >=3.7, <4
  22. Description-Content-Type: text/markdown
  23. [<img align="right" src="https://cdn.buymeacoffee.com/buttons/default-orange.png" width="217px" height="51x">](https://www.buymeacoffee.com/rsalmei)
  24. [<img align="right" alt="Donate with PayPal button" src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif">](https://www.paypal.com/donate?business=6SWSHEB5ZNS5N&no_recurring=0&item_name=I%27m+the+author+of+alive-progress%2C+clearly+and+about-time.+Thank+you+for+appreciating+my+work%21&currency_code=USD)
  25. # about-time
  26. ### A cool helper for tracking time and throughput of code blocks, with beautiful human friendly renditions.
  27. [![Coverage](https://img.shields.io/badge/coverage-100%25-green.svg)]()
  28. [![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://GitHub.com/rsalmei/about-time/graphs/commit-activity)
  29. [![PyPI version](https://img.shields.io/pypi/v/about-time.svg)](https://pypi.python.org/pypi/about-time/)
  30. [![PyPI pyversions](https://img.shields.io/pypi/pyversions/about-time.svg)](https://pypi.python.org/pypi/about-time/)
  31. [![PyPI status](https://img.shields.io/pypi/status/about-time.svg)](https://pypi.python.org/pypi/about-time/)
  32. [![PyPI Downloads](https://pepy.tech/badge/about-time)](https://pepy.tech/project/about-time)
  33. ## What does it do?
  34. Did you ever need to measure the duration of an operation? Yeah, this is easy.
  35. But how to:
  36. - measure the duration of two or more blocks at the same time, including the whole duration?
  37. - instrument a code to cleanly retrieve durations in one line, to log or send to time series databases?
  38. - easily see human friendly durations in *s* (seconds), *ms* (milliseconds), *µs* (microseconds) and even *ns* (nanoseconds)?
  39. - easily see human friendly counts with SI prefixes like *k*, *M*, *G*, *T*, etc?
  40. - measure the actual throughput of a block? (this is way harder, since it needs to measure both duration and number of iterations)
  41. - easily see human friendly throughputs in "/second", "/minute", "/hour" or even "/day", including SI prefixes?
  42. Yes, it can get tricky! More interesting details about [duration](https://github.com/rsalmei/about-time#the-human-duration-magic) and [throughput](https://github.com/rsalmei/about-time#the-human-throughput-magic).
  43. <br>If you'd tried to do it without these magic, it would probably get messy and immensely pollute the code being instrumented.
  44. I have the solution, behold!
  45. ```python
  46. from about_time import about_time
  47. def some_func():
  48. import time
  49. time.sleep(85e-3)
  50. return True
  51. def main():
  52. with about_time() as t1: # <-- use it like a context manager!
  53. t2 = about_time(some_func) # <-- use it with any callable!!
  54. t3 = about_time(x * 2 for x in range(56789)) # <-- use it with any iterable or generator!!!
  55. data = [x for x in t3] # then just iterate!
  56. print(f'total: {t1.duration_human}')
  57. print(f' some_func: {t2.duration_human} -> result: {t2.result}')
  58. print(f' generator: {t3.duration_human} -> {t3.count_human} elements, throughput: {t3.throughput_human}')
  59. ```
  60. This `main()` function prints:
  61. ```
  62. total: 95.6ms
  63. some_func: 89.7ms -> result: True
  64. generator: 5.79ms -> 56.8k elements, throughput: 9.81M/s
  65. ```
  66. How cool is that? 😲👏
  67. You can also get the duration in seconds if needed:
  68. ```
  69. In [7]: t1.duration
  70. Out[7]: 0.09556673200064251
  71. ```
  72. But `95.6ms` is way better, isn't it? The same with `count` and `throughput`!
  73. So, `about_time` measures code blocks, both time and throughput, and converts them to beautiful human friendly representations! 👏
  74. ## Get it
  75. Just install with pip:
  76. ```bash
  77. ❯ pip install about-time
  78. ```
  79. ## Use it
  80. There are three modes of operation: context manager, callable and throughput. Let's dive in.
  81. ### 1. Use it like a context manager:
  82. ```python
  83. from about_time import about_time
  84. with about_time() as t:
  85. # the code to be measured...
  86. # any lenghty block.
  87. print(f'The whole block took: {t.duration_human}')
  88. ```
  89. This way you can nicely wrap any amount of code.
  90. > In this mode, there are the basic fields `duration` and `duration_human`.
  91. ### 2. Use it with any callable:
  92. ```python
  93. from about_time import about_time
  94. t = about_time(some_func)
  95. print(f'The whole block took: {t.duration_human}')
  96. print(f'And the result was: {t.result}')
  97. ```
  98. This way you have a nice one liner, and do not need to increase the indent of your code.
  99. > In this mode, there is an additional field `result`, with the return of the callable.
  100. If the callable have params, you can use a `lambda` or (📌 new) simply send them:
  101. ```python
  102. def add(n, m):
  103. return n + m
  104. t = about_time(add, 1, 41)
  105. # or:
  106. t = about_time(add, n=1, m=41)
  107. # or even:
  108. t = about_time(lambda: add(1, 41))
  109. ```
  110. ### 3. Use it with any iterable or generator:
  111. ```python
  112. from about_time import about_time
  113. t = about_time(iterable)
  114. for item in t:
  115. # process item.
  116. print(f'The whole block took: {t.duration_human}')
  117. print(f'It was detected {t.count_human} elements')
  118. print(f'The throughput was: {t.throughput_human}')
  119. ```
  120. This way `about_time` also extracts the number of iterations, and with the measured duration it calculates the throughput of the whole loop! It's especially useful with generators, which do not have length.
  121. > In this mode, there are the additional fields `count`, `count_human`, `throughput` and `throughput_human`.
  122. Cool tricks under the hood:
  123. - you can use it even with generator expressions, anything that is iterable to python!
  124. - you can consume it not only in a `for` loop, but also in { list | dict | set } comprehensions, `map()`s, `filter()`s, `sum()`s, `max()`s, `list()`s, etc, thus any function that expects an iterator! 👏
  125. - the timer only starts when the first element is queried, so you can initialize whatever you need before entering the loop! 👏
  126. - the `count`/`count_human` and `throughput`/`throughput_human` fields are updated in **real time**, so you can use them even inside the loop!
  127. ## Features:
  128. According to the SI standard, there are 1000 bytes in a `kilobyte`.
  129. <br>There is another standard called IEC that has 1024 bytes in a `kibibyte`, but this is only useful when measuring things that are naturally a power of two, e.g. a stick of RAM.
  130. Be careful to not render IEC quantities with SI scaling, which would be incorrect. But I still support it, if you really want to ;)
  131. By default, this will use SI, `1000` divisor, and `no space` between values and scales/units. SI uses prefixes: `k`, `M`, `G`, `T`, `P`, `E`, `Z`, and `Y`.
  132. These are the optional features:
  133. - `iec` => use IEC instead of SI: `Ki`, `Mi`, `Gi`, `Ti`, `Pi`, `Ei`, `Zi`, `Yi` (implies `1024`);
  134. - `1024` => use `1024` divisor — if `iec` is not enabled, use prefixes: `K`, `M`, `G`, `T`, `P`, `E`, `Z`, and `Y` (note the upper 'K');
  135. - `space` => include a space between values and scales/units everywhere: `48 B` instead of `48B`, `15.6 µs` instead of `15.6µs`, and `12.4 kB/s` instead of `12.4kB/s`.
  136. To change them, just use the properties:
  137. ```python
  138. from about_time import FEATURES
  139. FEATURES.feature_1024
  140. FEATURES.feature_iec
  141. FEATURES.feature_space
  142. ```
  143. For example, to enable spaces between scales/units:
  144. ```python
  145. from about_time import FEATURES
  146. FEATURES.feature_space = True
  147. ```
  148. ## The human duration magic
  149. I've used just one key concept in designing the human duration features: cleanliness.
  150. > `3.44s` is more meaningful than `3.43584783784s`, and `14.1us` is much nicer than `.0000141233333s`.
  151. So what I do is: round values to at most two decimal places (three significant digits), and find the best scale unit to represent them, minimizing resulting values smaller than `1`. The search for the best unit considers even the rounding been applied!
  152. > `0.000999999` does not end up as `999.99us` (truncate) nor `1000.0us` (bad unit), but is auto-upgraded to the next unit `1.0ms`!
  153. The `duration_human` units change seamlessly from nanoseconds to hours.
  154. - values smaller than 60 seconds are always rendered as "num.D[D]unit", with one or two decimals;
  155. - from 1 minute onward it changes to "H:MM:SS".
  156. It feels much more humane, humm? ;)
  157. Some examples:
  158. | duration (float seconds) | duration_human |
  159. |:------------------------:|:--------------:|
  160. | .00000000185 | '1.85ns' |
  161. | .000000999996 | '1.00µs' |
  162. | .00001 | '10.0µs' |
  163. | .0000156 | '15.6µs' |
  164. | .01 | '10.0ms' |
  165. | .0141233333333 | '14.1ms' |
  166. | .1099999 | '110ms' |
  167. | .1599999 | '160ms' |
  168. | .8015 | '802ms' |
  169. | 3.434999 | '3.43s' |
  170. | 59.999 | '0:01:00' |
  171. | 68.5 | '0:01:08' |
  172. | 125.825 | '0:02:05' |
  173. | 4488.395 | '1:14:48' |
  174. ## The human throughput magic
  175. I've made the `throughput_human` with a similar logic. It is funny how much trickier "throughput" is to the human brain!
  176. > If something took `1165263 seconds` to handle `123 items`, how fast did it go? It's not obvious...
  177. It doesn't help even if we divide the duration by the number of items, `9473 seconds/item`, which still does not mean much. How fast was that? We can't say.
  178. <br>How many items did we do per time unit?
  179. > Oh, we just need to invert it, so `0,000105555569858 items/second`, there it is! 😂
  180. To make some sense of it we need to multiply that by 3600 (seconds in an hour) to get `0.38/h`, which is much better, and again by 24 (hours in a day) to finally get `9.12/d`!! Now we know how fast that process was! \o/ As you see, it's not easy at all.
  181. The `throughput_human` unit changes seamlessly from per-second, per-minute, per-hour, and per-day.
  182. <br>It also automatically inserts SI-prefixes, like k, M, and G. 👍
  183. | duration (float seconds) | number of elements | throughput_human |
  184. |:------------------------:|:------------------:|:----------------:|
  185. | 1\. | 10 | '10.0/s' |
  186. | 1\. | 2500 | '2.50k/s' |
  187. | 1\. | 1825000 | '1.82M/s' |
  188. | 2\. | 1 | '30.0/m' |
  189. | 2\. | 10 | '5.00/s' |
  190. | 1.981981981981982 | 11 | '5.55/s' |
  191. | 100\. | 10 | '6.00/m' |
  192. | 1600\. | 3 | '6.75/h' |
  193. | .99 | 1 | '1.01/s' |
  194. | 1165263\. | 123 | '9.12/d' |
  195. ## Accuracy
  196. `about_time` supports all versions of python, but in pythons >= `3.3` it performs even better, with much higher resolution and smaller propagation of errors, thanks to the new `time.perf_counter`. In older versions, it uses `time.time` as usual.
  197. ## Changelog highlights:
  198. - 4.2.1: makes fixed precision actually gain more resolution, when going from a default 1 to 2 decimals
  199. - 4.2.0: support for fixed precision, useful when one needs output without varying lengths; official Python 3.11 support
  200. - 4.1.0: enable to cache features within closures, to improve performance for https://github.com/rsalmei/alive-progress
  201. - 4.0.0: new version, modeled after my Rust implementation in https://crates.io/crates/human-repr; includes new global features, new objects for each operation, and especially, new simpler human friendly representations; supports Python 3.7+
  202. - 3.3.0: new interfaces for count_human and throughput_human; support more common Kbyte for base 2 (1024), leaving IEC one as an alternate
  203. - 3.2.2: support IEC kibibyte standard for base 2 (1024)
  204. - 3.2.1: support divisor in throughput_human
  205. - 3.2.0: both durations and throughputs now use 3 significant digits; throughputs now include SI-prefixes
  206. - 3.1.1: make `duration_human()` and `throughput_human()` available for external use
  207. - 3.1.0: include support for parameters in callable mode; official support for python 3.8, 3.9 and 3.10
  208. - 3.0.0: greatly improved the counter/throughput mode, with a single argument and working in real time
  209. - 2.0.0: feature complete, addition of callable and throughput modes
  210. - 1.0.0: first public release, context manager mode
  211. ## License
  212. This software is licensed under the MIT License. See the LICENSE file in the top distribution directory for the full license text.
  213. ---
  214. Maintaining an open source project is hard and time-consuming, and I've put much ❤️ and effort into this.
  215. If you've appreciated my work, you can back me up with a donation! Thank you 😊
  216. [<img align="right" src="https://cdn.buymeacoffee.com/buttons/default-orange.png" width="217px" height="51x">](https://www.buymeacoffee.com/rsalmei)
  217. [<img align="right" alt="Donate with PayPal button" src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif">](https://www.paypal.com/donate?business=6SWSHEB5ZNS5N&no_recurring=0&item_name=I%27m+the+author+of+alive-progress%2C+clearly+and+about-time.+Thank+you+for+appreciating+my+work%21&currency_code=USD)
  218. ---