diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index acc6ced..48ee030 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,15 +1,11 @@ -fail_fast: true - repos: - - repo: https://github.com/ambv/black - rev: 22.10.0 - hooks: - - id: black - - repo: https://gitlab.com/pycqa/flake8 - rev: 3.9.2 +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v2.3.0 hooks: - - id: flake8 - - repo: https://github.com/timothycrosley/isort - rev: 5.10.1 + - id: check-yaml + - id: end-of-file-fixer + - id: trailing-whitespace +- repo: https://github.com/psf/black + rev: 22.10.0 hooks: - - id: isort + - id: black diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..1f01168 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,12 @@ +{ + "python.analysis.typeCheckingMode": "basic", + "[python]": { + "editor.defaultFormatter": "ms-python.black-formatter" + }, + "python.formatting.provider": "none", + "python.testing.pytestArgs": [ + "tests" + ], + "python.testing.unittestEnabled": false, + "python.testing.pytestEnabled": true +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..25408dd --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) [year] [author] + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 5efed88..3a7a718 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,49 @@ -# Project Name +# Execy -Short project description +The ececy exec_time decorator is a utility that measures the execution time of a function. It can be used to easily track and log the time taken by a function to execute, both for synchronous and asynchronous functions. ## Table of Contents -- [Introduction](#introduction) -- [Features](#features) -- [Installation](#installation) -- [Usage](#usage) -- [Contributing](#contributing) -- [License](#license) +- [Execy](#execy) + - [Table of Contents](#table-of-contents) + - [Installation](#installation) + - [Usage](#usage) -## Introduction +## Installation -Provide an introduction to your project here. Explain what it does and why it's useful. +```bash +pip install execy +``` -## Features +## Usage -List the main features and functionalities of your project. +To use the exec_time decorator, follow these steps: +```python +from execy import exec_time +import asyncio +import time -## Installation +@exec_time +def sync_function(): + # Simulate a time-consuming synchronous task + time.sleep(2) -Provide instructions on how to install and set up your project. Include any dependencies and installation steps required. +@exec_time +async def async_function(): + # Simulate a time-consuming asynchronous task + await asyncio.sleep(2) -## Usage +# Call the decorated synchronous function +sync_function() + +# Call the decorated asynchronous function +asyncio.run(async_function()) +``` + +The result will be -Provide examples and explanations on how to use your project. Include code snippets or detailed instructions to help users get started. +```bash +Function 'sync_function' executed in 2.0001089572906494 seconds. +Function 'async_function' executed in 2.001432180404663 seconds. +``` diff --git a/pyproject.toml b/pyproject.toml index 1d8b2d1..d0d5b2d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,11 +1,11 @@ [project] -name = "exectime" +name = "execy" version = "0.1.0" description = "Add a short description here" authors = [ { name = "hglong16", email = "intihad.vuong@gmail.com" } ] -dependencies = [] +dependencies = ["pytest-asyncio~=0.21.0", "setuptools~=67.8.0", "wheel~=0.40.0", "twine~=4.0.2"] readme = "README.md" requires-python = ">= 3.8" @@ -13,20 +13,12 @@ requires-python = ">= 3.8" requires = ["hatchling"] build-backend = "hatchling.build" +[tool.rye.scripts] +build = "python setup.py sdist bdist_wheel" +upload = "twine upload dist/*" + [tool.rye] managed = true dev-dependencies = ["pytest~=7.3.1", "black~=23.3.0", "isort~=5.12.0"] [tool.hatch.metadata] allow-direct-references = true - -[tool.flake8] -max-line-length = 75 -extend-ignore = ["D203", "E203", "E251", "E266", "E302", "E305", "E401", "E402", "E501", "F401", "F403", "W503"] -exclude = [".git", "__pycache__", "dist"] -max-complexity = 10 - -[tool.isort] -atomic = true -profile = "black" -line_length = 75 -skip_gitignore = true diff --git a/requirements-dev.lock b/requirements-dev.lock index 19065bc..b0877f5 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -8,14 +8,46 @@ -e file:. black==23.3.0 +bleach==6.0.0 +certifi==2023.5.7 +cffi==1.15.1 +charset-normalizer==3.1.0 click==8.1.3 +cryptography==41.0.0 +docutils==0.20.1 exceptiongroup==1.1.1 +idna==3.4 +importlib-metadata==6.6.0 iniconfig==2.0.0 isort==5.12.0 +jaraco-classes==3.2.3 +jeepney==0.8.0 +keyring==23.13.1 +markdown-it-py==2.2.0 +mdurl==0.1.2 +more-itertools==9.1.0 mypy-extensions==1.0.0 packaging==23.1 pathspec==0.11.1 +pkginfo==1.9.6 platformdirs==3.5.1 pluggy==1.0.0 +pycparser==2.21 +pygments==2.15.1 pytest==7.3.1 +pytest-asyncio==0.21.0 +readme-renderer==37.3 +requests==2.31.0 +requests-toolbelt==1.0.0 +rfc3986==2.0.0 +rich==13.4.1 +secretstorage==3.3.3 +six==1.16.0 tomli==2.0.1 +twine==4.0.2 +urllib3==2.0.2 +webencodings==0.5.1 +wheel==0.40.0 +zipp==3.15.0 +# The following packages are considered to be unsafe in a requirements file: +setuptools==67.8.0 diff --git a/requirements.lock b/requirements.lock index aa6be96..0119f23 100644 --- a/requirements.lock +++ b/requirements.lock @@ -7,3 +7,41 @@ # all-features: false -e file:. +bleach==6.0.0 +certifi==2023.5.7 +cffi==1.15.1 +charset-normalizer==3.1.0 +cryptography==41.0.0 +docutils==0.20.1 +exceptiongroup==1.1.1 +idna==3.4 +importlib-metadata==6.6.0 +iniconfig==2.0.0 +jaraco-classes==3.2.3 +jeepney==0.8.0 +keyring==23.13.1 +markdown-it-py==2.2.0 +mdurl==0.1.2 +more-itertools==9.1.0 +packaging==23.1 +pkginfo==1.9.6 +pluggy==1.0.0 +pycparser==2.21 +pygments==2.15.1 +pytest==7.3.1 +pytest-asyncio==0.21.0 +readme-renderer==37.3 +requests==2.31.0 +requests-toolbelt==1.0.0 +rfc3986==2.0.0 +rich==13.4.1 +secretstorage==3.3.3 +six==1.16.0 +tomli==2.0.1 +twine==4.0.2 +urllib3==2.0.2 +webencodings==0.5.1 +wheel==0.40.0 +zipp==3.15.0 +# The following packages are considered to be unsafe in a requirements file: +setuptools==67.8.0 diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..f4534c3 --- /dev/null +++ b/setup.py @@ -0,0 +1,15 @@ +# setup.py + +from setuptools import setup, find_packages + +setup( + name="execy", + version="1.0.0", + author="hglong16", + author_email="intihad.vuong@gmail.com", + description="A package for measuring execution time", + packages=find_packages("src"), + package_dir={"": "src"}, + python_requires=">=3.6", + license="MIT", +) diff --git a/src/exectime/__init__.py b/src/exectime/__init__.py index 43a9d76..e69de29 100644 --- a/src/exectime/__init__.py +++ b/src/exectime/__init__.py @@ -1,2 +0,0 @@ -def hello(): - return "Hello from exectime!" diff --git a/src/exectime/exec.py b/src/exectime/exec.py new file mode 100644 index 0000000..485e638 --- /dev/null +++ b/src/exectime/exec.py @@ -0,0 +1,32 @@ +import time +import asyncio +from functools import wraps + + +def exec_time(func): + @wraps(func) + def wrapper(*args, **kwargs): + if asyncio.iscoroutinefunction(func): + return measure_execution_time_async(func, *args, **kwargs) + else: + return measure_execution_time_sync(func, *args, **kwargs) + + def measure_execution_time_sync(func, *args, **kwargs): + start_time = time.time() + result = func(*args, **kwargs) + end_time = time.time() + execution_time = end_time - start_time + wrapper.execution_time = execution_time # type: ignore + print(f"Function '{func.__name__}' executed in {execution_time} seconds.") + return result + + async def measure_execution_time_async(func, *args, **kwargs): + start_time = time.time() + result = await func(*args, **kwargs) + end_time = time.time() + execution_time = end_time - start_time + wrapper.execution_time = execution_time # type: ignore + print(f"Function '{func.__name__}' executed in {execution_time} seconds.") + return result + + return wrapper diff --git a/tests/test_exec.py b/tests/test_exec.py new file mode 100644 index 0000000..bfb8ee8 --- /dev/null +++ b/tests/test_exec.py @@ -0,0 +1,34 @@ +import time +import asyncio +import pytest +from exectime.exec import exec_time + + +@exec_time +async def async_function(): + # Simulate a time-consuming task asynchronously + await asyncio.sleep(2) + + +def test_execution_time(): + @exec_time + def time_consuming_function(): + # Simulate a time-consuming task + time.sleep(2) + + # Call the decorated function + time_consuming_function() + + # Assert that the execution time is within an acceptable range + assert time_consuming_function.execution_time >= 1.9 + assert time_consuming_function.execution_time <= 2.1 + + +@pytest.mark.asyncio +async def test_async_execution_time(): + # Call the decorated async function + await async_function() + + # Assert that the execution time is within an acceptable range + assert async_function.execution_time >= 1.9 + assert async_function.execution_time <= 2.1