etimedecorator


Nameetimedecorator JSON
Version 1.0.1 PyPI version JSON
download
home_pagehttps://github.com/rabuchaim/etimedecorator
SummaryeTimeDecorator is a set of 3 elapsed timer decorators for Python 3.x and PyPy3 to measure the execution time of a function that executes hundreds/thousands of times per second. It also works with asyncio. Records the minimum, average and maximum elapsed time of functions and calculates the percentile.
upload_time2024-07-21 04:54:54
maintainerRicardo Abuchaim
docs_urlNone
authorRicardo Abuchaim
requires_python>=3.7
licenseMIT
keywords elapsed elapsed timer elapsedtimer elapsedtime elapsed time timer perf_counter monotonic timeit timing decorator debug performance
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # eTimeDecorator v1.0.1 (Elapsed Time Decorator)

eTimeDecorator is a set of 3 elapsed timer decorators for Python 3.x and PyPy3 to measure the execution time of a function that executes hundreds/thousands of times per second. It also works with asyncio. Records the minimum, average and maximum elapsed time of functions and calculates the percentile.

![](https://raw.githubusercontent.com/rabuchaim/etimedecorator/main/example.png)

## Installation

```
pip install etimedecorator
```

## Decorators

- **`@elapsedTimeDecorator(decimal_places:int=9,print_args:bool=False)`**: 

    It is a very simple decorator and displays the execution time of the decorated function. **It is not suitable** for functions that are called hundreds or thousands of times per second. You can also define the number of decimal places to be displayed in statistics using the `decimal_places` parameter, by default the number of decimal places is 9. And the `print_args` parameter prints the arguments (*args and **kwargs) passed to the function.
    
    - Output: *Elapsed Time for method_name(): 0.000001228 sec*

- **`@elapsedTimeAverageDecorator(window_size:int=1000,decimal_places:int=9,print_args:bool=False)`**:

    This decorator is already suitable for reentrant functions with thousands of executions per second. Accepts the `window_size` parameters that indicate after how many executions the minimum, average and maximum execution time statistics will be displayed. You can also define the number of decimal places to be displayed in statistics using the `decimal_places` parameter, by default the number of decimal places is 9. And the `print_args` parameter prints the arguments (*args and **kwargs) passed to the function.

    - Output: *Elapsed Time for method_name() - 1000 calls - Min:0.000000454 / Avg:0.000000597 / Max:0.000038203*

- **`@elapsedTimePercentileDecorator(window_size:int=1000,decimal_places:int=9,print_args:bool=False)`**

    This decorator is also suitable for reentrant functions with thousands of executions per second but displays the percentile calculation of the functions' execution time. Accepts the `window_size` parameters that indicate after how many executions the minimum, average and maximum execution time statistics will be displayed. You can also define the number of decimal places to be displayed in statistics using the `decimal_places` parameter, by default the number of decimal places is 9 *Because percentile calculation requires an ordered list of execution times, this method is slightly slower than the @elapsedTimeAverageDecorator method.*. And the `print_args` parameter prints the arguments (*args and **kwargs) passed to the function. 

    - Output: *Elapsed Time for method_name() 1000 calls - Min:0.000000517 Avg:0.000000750 Max:0.000022660 - 50%:0.000000624 75%:0.000000688 90%:0.000001000 99%:0.000002410*


## Usage example

Let's imagine an application that converts integers to IP addresses. If you are considering sporadic executions, you can use any of these functions, but if they are executed hundreds or thousands of times per second, you need to evaluate which of these functions performs best.

Here we have 3 ways to do this and we will measure which of these 3 ways performs better in our example. We will use the 'ipaddress' library and another function that uses 'socket' and 'struct', and a *roots* function do this calc. Let's run each of them 4000 times and evaluate performance after every 1000 runs using randomic numbers without cache. It will also be executed another 8000 times with asyncio.

We will only read the returned value and will not print anything on the screen. 

```python
import ipaddress, socket, struct, random, asyncio
from etimedecorator import elapsedTimeDecorator, elapsedTimeAverageDecorator, elapsedTimePercentileDecorator

@elapsedTimeDecorator()
def roots_int2ip(iplong):
    return f"{(iplong >> 24) & 0xFF}.{(iplong >> 16) & 0xFF}.{(iplong >> 8) & 0xFF}.{iplong & 0xFF}"

@elapsedTimeAverageDecorator(window_size=1000,print_args=True) # Will print the 'iplong' value passed to the function as *args
def ipaddress_int2ip(iplong)->str:
    return str(ipaddress.ip_address(iplong))

@elapsedTimePercentileDecorator(window_size=1000,print_args=True) # Will print the 'iplong' value passed to the function as **kwargs
def struct_int2ip(iplong=iplong)->str:
    return socket.inet_ntoa(struct.pack('>L', iplong))

##──── Using the roots method 
ip = roots_int2ip(random.randrange(16777216,3758096383))
ip = roots_int2ip(random.randrange(16777216,3758096383))
ip = roots_int2ip(random.randrange(16777216,3758096383))
ip = roots_int2ip(random.randrange(16777216,3758096383))

print("- "*40)

##──── Using ipaddress library 
for I in range(4000):
    ip = ipaddress_int2ip(random.randrange(16777216,3758096383)) # from 1.0.0.0 to 223.255.255.255
    
##──── Using socket-struct libraries
for I in range(4000):
    ip = struct_int2ip(random.randrange(16777216,3758096383)) # from 1.0.0.0 to 223.255.255.255

print("- "*40)

##──── Now use ipaddress and socket-struct with ASYNCIO 
async def example_async():
    tasks = [ async_ipaddress_int2ip(random.randrange(16777216, 3758096383))
        for _ in range(4000)
    ]
    results = await asyncio.gather(*tasks)

    tasks = [ async_struct_int2ip(random.randrange(16777216, 3758096383))
        for _ in range(4000)
    ]
    results = await asyncio.gather(*tasks)

@elapsedTimeAverageDecorator(window_size=1000)
async def async_ipaddress_int2ip(iplong)->str:
    return str(ipaddress.ip_address(iplong))

@elapsedTimePercentileDecorator(window_size=1000)
async def async_struct_int2ip(iplong)->str:
    return socket.inet_ntoa(struct.pack('>L', iplong))

asyncio.run(example_async())

```
Output:
```bash
Elapsed Time for roots_int2ip(): 0.000002739 sec
Elapsed Time for roots_int2ip(): 0.000002334 sec
Elapsed Time for roots_int2ip(): 0.000001023 sec
Elapsed Time for roots_int2ip(): 0.000006749 sec
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Elapsed Time for ipaddress_int2ip((3509596061,)) - 1000 calls - Min:0.000000920 / Avg:0.000001123 / Max:0.000011491
Elapsed Time for ipaddress_int2ip((3292303103,)) - 1000 calls - Min:0.000000920 / Avg:0.000001339 / Max:0.000016721
Elapsed Time for ipaddress_int2ip((1861348088,)) - 1000 calls - Min:0.000000905 / Avg:0.000001128 / Max:0.000031977
Elapsed Time for ipaddress_int2ip((1955570310,)) - 1000 calls - Min:0.000000905 / Avg:0.000001010 / Max:0.000031977
Elapsed Time for struct_int2ip({'iplong':3734307021}) 1000 calls - Min:0.000000543 Avg:0.000000724 Max:0.000041515 - 50%:0.000000603 75%:0.000000622 90%:0.000000647 99%:0.000001843
Elapsed Time for struct_int2ip({'iplong':1647016321}) 1000 calls - Min:0.000000499 Avg:0.000000649 Max:0.000041515 - 50%:0.000000560 75%:0.000000573 90%:0.000000603 99%:0.000001737
Elapsed Time for struct_int2ip({'iplong':2504612232}) 1000 calls - Min:0.000000472 Avg:0.000000582 Max:0.000041515 - 50%:0.000000562 75%:0.000000580 90%:0.000000601 99%:0.000000983
Elapsed Time for struct_int2ip({'iplong':2341449854}) 1000 calls - Min:0.000000472 Avg:0.000000604 Max:0.000041515 - 50%:0.000000533 75%:0.000000564 90%:0.000000818 99%:0.000001142
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Elapsed Time for async_ipaddress_int2ip() - 1000 calls - Min:0.000000123 / Avg:0.000000253 / Max:0.000012031
Elapsed Time for async_ipaddress_int2ip() - 1000 calls - Min:0.000000116 / Avg:0.000000211 / Max:0.000012031
Elapsed Time for async_ipaddress_int2ip() - 1000 calls - Min:0.000000116 / Avg:0.000000248 / Max:0.000012031
Elapsed Time for async_ipaddress_int2ip() - 1000 calls - Min:0.000000116 / Avg:0.000000223 / Max:0.000015060
Elapsed Time for async_struct_int2ip() 1000 calls - Min:0.000000133 Avg:0.000000225 Max:0.000009146 - 50%:0.000000165 75%:0.000000170 90%:0.000000187 99%:0.000001524
Elapsed Time for async_struct_int2ip() 1000 calls - Min:0.000000136 Avg:0.000000198 Max:0.000011554 - 50%:0.000000165 75%:0.000000170 90%:0.000000174 99%:0.000000497
Elapsed Time for async_struct_int2ip() 1000 calls - Min:0.000000136 Avg:0.000000250 Max:0.000014483 - 50%:0.000000162 75%:0.000000170 90%:0.000000175 99%:0.000001503
Elapsed Time for async_struct_int2ip() 1000 calls - Min:0.000000136 Avg:0.000000306 Max:0.000018371 - 50%:0.000000174 75%:0.000000178 90%:0.000000184 99%:0.000003273
```
The percentile indicated in the fields 50%, 75%, 90% and 99% means that 50% of executions are below the indicated value, in the other field it indicates that 75% of executions are below the indicated value, and so on.

As we can see, the function struct_int2ip() is faster than the ipaddress_int2ip() and roots_int2ip() function. All of this was measured without having to place codes and make calculations in the middle of the function code. When putting it into production, just comment or delete the decorator call.

And in the last 8000 executions, we can see that with asyncio the average elapsed time was much better than synchronously, and we can also see that the two functions async_ipaddress_int2ip() and async_struct_int2ip() obtained practically the same performance.

This decorator uses the time.monotonic() function which is not impacted by machine time changes.

## Sugestions, feedbacks, bugs...

E-mail me: ricardoabuchaim at gmail.com

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/rabuchaim/etimedecorator",
    "name": "etimedecorator",
    "maintainer": "Ricardo Abuchaim",
    "docs_url": null,
    "requires_python": ">=3.7",
    "maintainer_email": "ricardoabuchaim@gmail.com",
    "keywords": "elapsed, elapsed timer, elapsedtimer, elapsedtime, elapsed time, timer, perf_counter, monotonic, timeit, timing, decorator, debug, performance",
    "author": "Ricardo Abuchaim",
    "author_email": "ricardoabuchaim@gmail.com",
    "download_url": "https://files.pythonhosted.org/packages/75/a3/44c4ab003736a1ef038c70a43c044f0a9f0ea3d94d6b92b31fe0a5210cc2/etimedecorator-1.0.1.tar.gz",
    "platform": null,
    "description": "# eTimeDecorator v1.0.1 (Elapsed Time Decorator)\n\neTimeDecorator is a set of 3 elapsed timer decorators for Python 3.x and PyPy3 to measure the execution time of a function that executes hundreds/thousands of times per second. It also works with asyncio. Records the minimum, average and maximum elapsed time of functions and calculates the percentile.\n\n![](https://raw.githubusercontent.com/rabuchaim/etimedecorator/main/example.png)\n\n## Installation\n\n```\npip install etimedecorator\n```\n\n## Decorators\n\n- **`@elapsedTimeDecorator(decimal_places:int=9,print_args:bool=False)`**: \n\n    It is a very simple decorator and displays the execution time of the decorated function. **It is not suitable** for functions that are called hundreds or thousands of times per second. You can also define the number of decimal places to be displayed in statistics using the `decimal_places` parameter, by default the number of decimal places is 9. And the `print_args` parameter prints the arguments (*args and **kwargs) passed to the function.\n    \n    - Output: *Elapsed Time for method_name(): 0.000001228 sec*\n\n- **`@elapsedTimeAverageDecorator(window_size:int=1000,decimal_places:int=9,print_args:bool=False)`**:\n\n    This decorator is already suitable for reentrant functions with thousands of executions per second. Accepts the `window_size` parameters that indicate after how many executions the minimum, average and maximum execution time statistics will be displayed. You can also define the number of decimal places to be displayed in statistics using the `decimal_places` parameter, by default the number of decimal places is 9. And the `print_args` parameter prints the arguments (*args and **kwargs) passed to the function.\n\n    - Output: *Elapsed Time for method_name() - 1000 calls - Min:0.000000454 / Avg:0.000000597 / Max:0.000038203*\n\n- **`@elapsedTimePercentileDecorator(window_size:int=1000,decimal_places:int=9,print_args:bool=False)`**\n\n    This decorator is also suitable for reentrant functions with thousands of executions per second but displays the percentile calculation of the functions' execution time. Accepts the `window_size` parameters that indicate after how many executions the minimum, average and maximum execution time statistics will be displayed. You can also define the number of decimal places to be displayed in statistics using the `decimal_places` parameter, by default the number of decimal places is 9 *Because percentile calculation requires an ordered list of execution times, this method is slightly slower than the @elapsedTimeAverageDecorator method.*. And the `print_args` parameter prints the arguments (*args and **kwargs) passed to the function. \n\n    - Output: *Elapsed Time for method_name() 1000 calls - Min:0.000000517 Avg:0.000000750 Max:0.000022660 - 50%:0.000000624 75%:0.000000688 90%:0.000001000 99%:0.000002410*\n\n\n## Usage example\n\nLet's imagine an application that converts integers to IP addresses. If you are considering sporadic executions, you can use any of these functions, but if they are executed hundreds or thousands of times per second, you need to evaluate which of these functions performs best.\n\nHere we have 3 ways to do this and we will measure which of these 3 ways performs better in our example. We will use the 'ipaddress' library and another function that uses 'socket' and 'struct', and a *roots* function do this calc. Let's run each of them 4000 times and evaluate performance after every 1000 runs using randomic numbers without cache. It will also be executed another 8000 times with asyncio.\n\nWe will only read the returned value and will not print anything on the screen. \n\n```python\nimport ipaddress, socket, struct, random, asyncio\nfrom etimedecorator import elapsedTimeDecorator, elapsedTimeAverageDecorator, elapsedTimePercentileDecorator\n\n@elapsedTimeDecorator()\ndef roots_int2ip(iplong):\n    return f\"{(iplong >> 24) & 0xFF}.{(iplong >> 16) & 0xFF}.{(iplong >> 8) & 0xFF}.{iplong & 0xFF}\"\n\n@elapsedTimeAverageDecorator(window_size=1000,print_args=True) # Will print the 'iplong' value passed to the function as *args\ndef ipaddress_int2ip(iplong)->str:\n    return str(ipaddress.ip_address(iplong))\n\n@elapsedTimePercentileDecorator(window_size=1000,print_args=True) # Will print the 'iplong' value passed to the function as **kwargs\ndef struct_int2ip(iplong=iplong)->str:\n    return socket.inet_ntoa(struct.pack('>L', iplong))\n\n##\u2500\u2500\u2500\u2500 Using the roots method \nip = roots_int2ip(random.randrange(16777216,3758096383))\nip = roots_int2ip(random.randrange(16777216,3758096383))\nip = roots_int2ip(random.randrange(16777216,3758096383))\nip = roots_int2ip(random.randrange(16777216,3758096383))\n\nprint(\"- \"*40)\n\n##\u2500\u2500\u2500\u2500 Using ipaddress library \nfor I in range(4000):\n    ip = ipaddress_int2ip(random.randrange(16777216,3758096383)) # from 1.0.0.0 to 223.255.255.255\n    \n##\u2500\u2500\u2500\u2500 Using socket-struct libraries\nfor I in range(4000):\n    ip = struct_int2ip(random.randrange(16777216,3758096383)) # from 1.0.0.0 to 223.255.255.255\n\nprint(\"- \"*40)\n\n##\u2500\u2500\u2500\u2500 Now use ipaddress and socket-struct with ASYNCIO \nasync def example_async():\n    tasks = [ async_ipaddress_int2ip(random.randrange(16777216, 3758096383))\n        for _ in range(4000)\n    ]\n    results = await asyncio.gather(*tasks)\n\n    tasks = [ async_struct_int2ip(random.randrange(16777216, 3758096383))\n        for _ in range(4000)\n    ]\n    results = await asyncio.gather(*tasks)\n\n@elapsedTimeAverageDecorator(window_size=1000)\nasync def async_ipaddress_int2ip(iplong)->str:\n    return str(ipaddress.ip_address(iplong))\n\n@elapsedTimePercentileDecorator(window_size=1000)\nasync def async_struct_int2ip(iplong)->str:\n    return socket.inet_ntoa(struct.pack('>L', iplong))\n\nasyncio.run(example_async())\n\n```\nOutput:\n```bash\nElapsed Time for roots_int2ip(): 0.000002739 sec\nElapsed Time for roots_int2ip(): 0.000002334 sec\nElapsed Time for roots_int2ip(): 0.000001023 sec\nElapsed Time for roots_int2ip(): 0.000006749 sec\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\nElapsed Time for ipaddress_int2ip((3509596061,)) - 1000 calls - Min:0.000000920 / Avg:0.000001123 / Max:0.000011491\nElapsed Time for ipaddress_int2ip((3292303103,)) - 1000 calls - Min:0.000000920 / Avg:0.000001339 / Max:0.000016721\nElapsed Time for ipaddress_int2ip((1861348088,)) - 1000 calls - Min:0.000000905 / Avg:0.000001128 / Max:0.000031977\nElapsed Time for ipaddress_int2ip((1955570310,)) - 1000 calls - Min:0.000000905 / Avg:0.000001010 / Max:0.000031977\nElapsed Time for struct_int2ip({'iplong':3734307021}) 1000 calls - Min:0.000000543 Avg:0.000000724 Max:0.000041515 - 50%:0.000000603 75%:0.000000622 90%:0.000000647 99%:0.000001843\nElapsed Time for struct_int2ip({'iplong':1647016321}) 1000 calls - Min:0.000000499 Avg:0.000000649 Max:0.000041515 - 50%:0.000000560 75%:0.000000573 90%:0.000000603 99%:0.000001737\nElapsed Time for struct_int2ip({'iplong':2504612232}) 1000 calls - Min:0.000000472 Avg:0.000000582 Max:0.000041515 - 50%:0.000000562 75%:0.000000580 90%:0.000000601 99%:0.000000983\nElapsed Time for struct_int2ip({'iplong':2341449854}) 1000 calls - Min:0.000000472 Avg:0.000000604 Max:0.000041515 - 50%:0.000000533 75%:0.000000564 90%:0.000000818 99%:0.000001142\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\nElapsed Time for async_ipaddress_int2ip() - 1000 calls - Min:0.000000123 / Avg:0.000000253 / Max:0.000012031\nElapsed Time for async_ipaddress_int2ip() - 1000 calls - Min:0.000000116 / Avg:0.000000211 / Max:0.000012031\nElapsed Time for async_ipaddress_int2ip() - 1000 calls - Min:0.000000116 / Avg:0.000000248 / Max:0.000012031\nElapsed Time for async_ipaddress_int2ip() - 1000 calls - Min:0.000000116 / Avg:0.000000223 / Max:0.000015060\nElapsed Time for async_struct_int2ip() 1000 calls - Min:0.000000133 Avg:0.000000225 Max:0.000009146 - 50%:0.000000165 75%:0.000000170 90%:0.000000187 99%:0.000001524\nElapsed Time for async_struct_int2ip() 1000 calls - Min:0.000000136 Avg:0.000000198 Max:0.000011554 - 50%:0.000000165 75%:0.000000170 90%:0.000000174 99%:0.000000497\nElapsed Time for async_struct_int2ip() 1000 calls - Min:0.000000136 Avg:0.000000250 Max:0.000014483 - 50%:0.000000162 75%:0.000000170 90%:0.000000175 99%:0.000001503\nElapsed Time for async_struct_int2ip() 1000 calls - Min:0.000000136 Avg:0.000000306 Max:0.000018371 - 50%:0.000000174 75%:0.000000178 90%:0.000000184 99%:0.000003273\n```\nThe percentile indicated in the fields 50%, 75%, 90% and 99% means that 50% of executions are below the indicated value, in the other field it indicates that 75% of executions are below the indicated value, and so on.\n\nAs we can see, the function struct_int2ip() is faster than the ipaddress_int2ip() and roots_int2ip() function. All of this was measured without having to place codes and make calculations in the middle of the function code. When putting it into production, just comment or delete the decorator call.\n\nAnd in the last 8000 executions, we can see that with asyncio the average elapsed time was much better than synchronously, and we can also see that the two functions async_ipaddress_int2ip() and async_struct_int2ip() obtained practically the same performance.\n\nThis decorator uses the time.monotonic() function which is not impacted by machine time changes.\n\n## Sugestions, feedbacks, bugs...\n\nE-mail me: ricardoabuchaim at gmail.com\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "eTimeDecorator is a set of 3 elapsed timer decorators for Python 3.x and PyPy3 to measure the execution time of a function that executes hundreds/thousands of times per second. It also works with asyncio. Records the minimum, average and maximum elapsed time of functions and calculates the percentile.",
    "version": "1.0.1",
    "project_urls": {
        "Homepage": "https://github.com/rabuchaim/etimedecorator",
        "Issue Tracker": "https://github.com/rabuchaim/etimedecorator/issues",
        "Source code": "https://github.com/rabuchaim/etimedecorator"
    },
    "split_keywords": [
        "elapsed",
        " elapsed timer",
        " elapsedtimer",
        " elapsedtime",
        " elapsed time",
        " timer",
        " perf_counter",
        " monotonic",
        " timeit",
        " timing",
        " decorator",
        " debug",
        " performance"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "ee84bcd7ac980596b0e087d68d25e7166ef06041425b405d29b8abd4c44fddbb",
                "md5": "583a219a20c0a5a0ce5002890dda665a",
                "sha256": "dc0cb8c8668e9b7573acf1c5aa0a96dee41ce0afed740cdd789f43de575cb42f"
            },
            "downloads": -1,
            "filename": "etimedecorator-1.0.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "583a219a20c0a5a0ce5002890dda665a",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.7",
            "size": 10665,
            "upload_time": "2024-07-21T04:54:52",
            "upload_time_iso_8601": "2024-07-21T04:54:52.463580Z",
            "url": "https://files.pythonhosted.org/packages/ee/84/bcd7ac980596b0e087d68d25e7166ef06041425b405d29b8abd4c44fddbb/etimedecorator-1.0.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "75a344c4ab003736a1ef038c70a43c044f0a9f0ea3d94d6b92b31fe0a5210cc2",
                "md5": "6d135892cdbe0ba8e6a84a82aeef8a7a",
                "sha256": "aa33e1cc3eb5bf330dbb151c19073f71dc21dbca615e227fef82b6baa78f94a9"
            },
            "downloads": -1,
            "filename": "etimedecorator-1.0.1.tar.gz",
            "has_sig": false,
            "md5_digest": "6d135892cdbe0ba8e6a84a82aeef8a7a",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.7",
            "size": 7683,
            "upload_time": "2024-07-21T04:54:54",
            "upload_time_iso_8601": "2024-07-21T04:54:54.046240Z",
            "url": "https://files.pythonhosted.org/packages/75/a3/44c4ab003736a1ef038c70a43c044f0a9f0ea3d94d6b92b31fe0a5210cc2/etimedecorator-1.0.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-07-21 04:54:54",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "rabuchaim",
    "github_project": "etimedecorator",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "etimedecorator"
}
        
Elapsed time: 0.29966s