%global _empty_manifest_terminate_build 0 Name: python-limiter Version: 0.3.1 Release: 1 Summary: ⏲️ Easy rate limiting for Python. Rate limiting async and thread-safe decorators and context managers that use a token bucket algorithm. License: AGPL-3.0 URL: https://github.com/alexdelorenzo/limiter Source0: https://mirrors.nju.edu.cn/pypi/web/packages/93/14/cf5fc005b55a8678fb2553cf132217ccb120c319f5975b2bee86b4381e4f/limiter-0.3.1.tar.gz BuildArch: noarch Requires: python3-strenum Requires: python3-token-bucket %description # ⏲️ Easy rate limiting for Python `limiter` makes it easy to add [rate limiting](https://en.wikipedia.org/wiki/Rate_limiting) to Python projects, using a [token bucket](https://en.wikipedia.org/wiki/Token_bucket) algorithm. `limiter` can provide Python projects and scripts with: - Rate limiting thread-safe [decorators](https://www.python.org/dev/peps/pep-0318/) - Rate limiting async decorators - Rate limiting thread-safe [context managers](https://www.python.org/dev/peps/pep-0343/) - Rate limiting [async context managers](https://www.python.org/dev/peps/pep-0492/#asynchronous-context-managers-and-async-with) Here are some features and benefits of using `limiter`: - Easily control burst and average request rates - It is [thread-safe, with no need for a timer thread](https://en.wikipedia.org/wiki/Generic_cell_rate_algorithm#Comparison_with_the_token_bucket) - It adds [jitter](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/) to help with contention - It has a simple API that takes advantage of Python's features, idioms and [type hinting](https://www.python.org/dev/peps/pep-0483/) ## Example Here's an example of using a limiter as a decorator and context manager: ```python from aiohttp import ClientSession from limiter import Limiter limit_downloads = Limiter(rate=2, capacity=5, consume=2) @limit_downloads async def download_image(url: str) -> bytes: async with ClientSession() as session, session.get(url) as response: return await response.read() async def download_page(url: str) -> str: async with ( ClientSession() as session, limit_downloads, session.get(url) as response ): return await response.text() ``` # Usage You can define limiters and use them dynamically across your project. **Note**: If you're using Python version `3.9.x` or below, check out [the documentation for version `0.2.0` of `limiter` here](https://github.com/alexdelorenzo/limiter/blob/master/README-0.2.0.md). ### Limiting blocks of code `limiter` can rate limit all Python callables, and limiters can be used as context managers. You can define a limiter with a set refresh `rate` and total token `capacity`. You can set the amount of tokens to consume dynamically with `consume`, and the `bucket` parameter sets the bucket to consume tokens from: ```python3 from limiter import Limiter REFRESH_RATE: int = 2 BURST_RATE: int = 3 MSG_BUCKET: str = 'messages' limiter: Limiter = Limiter(rate=REFRESH_RATE, capacity=BURST_RATE) limit_msgs: Limiter = limiter(bucket=MSG_BUCKET) @limiter def download_page(url: str) -> bytes: ... @limiter(consume=2) async def download_page(url: str) -> bytes: ... def send_page(page: bytes): with limiter(consume=1.5, bucket=MSG_BUCKET): ... async def send_page(page: bytes): async with limit_msgs: ... @limit_msgs(consume=3) def send_email(to: str): ... async def send_email(to: str): async with limiter(bucket=MSG_BUCKET): ... ``` In the example above, both `limiter` and `limit_msgs` share the same limiter. The only difference is that `limit_msgs` will take tokens from the `MSG_BUCKET` bucket by default. ```python3 assert limiter.limiter is limit_msgs.limiter assert limiter.bucket != limit_msgs.bucket assert limiter != limit_msgs ``` ### Creating new limiters You can reuse existing limiters in your code, and you can create new limiters from the parameters of an existing limiter using the `new()` method. Or, you can define a new limiter entirely: ```python # you can reuse existing limiters limit_downloads: Limiter = limiter(consume=2) # you can use the settings from an existing limiter in a new limiter limit_downloads: Limiter = limiter.new(consume=2) # or you can simply define a new limiter limit_downloads: Limiter = Limiter(REFRESH_RATE, BURST_RATE, consume=2) @limit_downloads def download_page(url: str) -> bytes: ... @limit_downloads async def download_page(url: str) -> bytes: ... def download_image(url: str) -> bytes: with limit_downloads: ... async def download_image(url: str) -> bytes: async with limit_downloads: ... ``` Let's look at the difference between reusing an existing limiter, and creating new limiters with the `new()` method: ```python3 limiter_a: Limiter = limiter(consume=2) limiter_b: Limiter = limiter.new(consume=2) limiter_c: Limiter = Limiter(REFRESH_RATE, BURST_RATE, consume=2) assert limiter_a != limiter assert limiter_a != limiter_b != limiter_c assert limiter_a != limiter_b assert limiter_a.limiter is limiter.limiter assert limiter_a.limiter is not limiter_b.limiter assert limiter_a.attrs == limiter_b.attrs == limiter_c.attrs ``` The only things that are equivalent between the three new limiters above are the limiters' attributes, like the `rate`, `capacity`, and `consume` attributes. ### Creating anonymous, or single-use, limiters You don't have to assign `Limiter` objects to variables. Anonymous limiters don't share a token bucket like named limiters can. They work well when you don't have a reason to share a limiter between two or more blocks of code, and when a limiter has a single or independent purpose. `limiter`, after version `v0.3.0`, ships with a `limit` type alias for `Limiter`: ```python3 from limiter import limit @limit(capacity=2, consume=2) async def send_message(): ... async def upload_image(): async with limit(capacity=3) as limiter: ... ``` The above is equivalent to the below: ```python3 from limiter import Limiter @Limiter(capacity=2, consume=2) async def send_message(): ... async def upload_image(): async with Limiter(capacity=3) as limiter: ... ``` Both `limit` and `Limiter` are the same object: ```python3 assert limit is Limiter ``` # Installation ## Requirements - Python 3.10+ for versions `0.3.0` and up - [Python 3.7+ for versions below `0.3.0`](https://github.com/alexdelorenzo/limiter/blob/master/README-0.2.0.md) ## Install via PyPI ```bash $ python3 -m pip install limiter ``` # License See [`LICENSE`](/LICENSE). If you'd like to use this project with a different license, please get in touch. %package -n python3-limiter Summary: ⏲️ Easy rate limiting for Python. Rate limiting async and thread-safe decorators and context managers that use a token bucket algorithm. Provides: python-limiter BuildRequires: python3-devel BuildRequires: python3-setuptools BuildRequires: python3-pip %description -n python3-limiter # ⏲️ Easy rate limiting for Python `limiter` makes it easy to add [rate limiting](https://en.wikipedia.org/wiki/Rate_limiting) to Python projects, using a [token bucket](https://en.wikipedia.org/wiki/Token_bucket) algorithm. `limiter` can provide Python projects and scripts with: - Rate limiting thread-safe [decorators](https://www.python.org/dev/peps/pep-0318/) - Rate limiting async decorators - Rate limiting thread-safe [context managers](https://www.python.org/dev/peps/pep-0343/) - Rate limiting [async context managers](https://www.python.org/dev/peps/pep-0492/#asynchronous-context-managers-and-async-with) Here are some features and benefits of using `limiter`: - Easily control burst and average request rates - It is [thread-safe, with no need for a timer thread](https://en.wikipedia.org/wiki/Generic_cell_rate_algorithm#Comparison_with_the_token_bucket) - It adds [jitter](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/) to help with contention - It has a simple API that takes advantage of Python's features, idioms and [type hinting](https://www.python.org/dev/peps/pep-0483/) ## Example Here's an example of using a limiter as a decorator and context manager: ```python from aiohttp import ClientSession from limiter import Limiter limit_downloads = Limiter(rate=2, capacity=5, consume=2) @limit_downloads async def download_image(url: str) -> bytes: async with ClientSession() as session, session.get(url) as response: return await response.read() async def download_page(url: str) -> str: async with ( ClientSession() as session, limit_downloads, session.get(url) as response ): return await response.text() ``` # Usage You can define limiters and use them dynamically across your project. **Note**: If you're using Python version `3.9.x` or below, check out [the documentation for version `0.2.0` of `limiter` here](https://github.com/alexdelorenzo/limiter/blob/master/README-0.2.0.md). ### Limiting blocks of code `limiter` can rate limit all Python callables, and limiters can be used as context managers. You can define a limiter with a set refresh `rate` and total token `capacity`. You can set the amount of tokens to consume dynamically with `consume`, and the `bucket` parameter sets the bucket to consume tokens from: ```python3 from limiter import Limiter REFRESH_RATE: int = 2 BURST_RATE: int = 3 MSG_BUCKET: str = 'messages' limiter: Limiter = Limiter(rate=REFRESH_RATE, capacity=BURST_RATE) limit_msgs: Limiter = limiter(bucket=MSG_BUCKET) @limiter def download_page(url: str) -> bytes: ... @limiter(consume=2) async def download_page(url: str) -> bytes: ... def send_page(page: bytes): with limiter(consume=1.5, bucket=MSG_BUCKET): ... async def send_page(page: bytes): async with limit_msgs: ... @limit_msgs(consume=3) def send_email(to: str): ... async def send_email(to: str): async with limiter(bucket=MSG_BUCKET): ... ``` In the example above, both `limiter` and `limit_msgs` share the same limiter. The only difference is that `limit_msgs` will take tokens from the `MSG_BUCKET` bucket by default. ```python3 assert limiter.limiter is limit_msgs.limiter assert limiter.bucket != limit_msgs.bucket assert limiter != limit_msgs ``` ### Creating new limiters You can reuse existing limiters in your code, and you can create new limiters from the parameters of an existing limiter using the `new()` method. Or, you can define a new limiter entirely: ```python # you can reuse existing limiters limit_downloads: Limiter = limiter(consume=2) # you can use the settings from an existing limiter in a new limiter limit_downloads: Limiter = limiter.new(consume=2) # or you can simply define a new limiter limit_downloads: Limiter = Limiter(REFRESH_RATE, BURST_RATE, consume=2) @limit_downloads def download_page(url: str) -> bytes: ... @limit_downloads async def download_page(url: str) -> bytes: ... def download_image(url: str) -> bytes: with limit_downloads: ... async def download_image(url: str) -> bytes: async with limit_downloads: ... ``` Let's look at the difference between reusing an existing limiter, and creating new limiters with the `new()` method: ```python3 limiter_a: Limiter = limiter(consume=2) limiter_b: Limiter = limiter.new(consume=2) limiter_c: Limiter = Limiter(REFRESH_RATE, BURST_RATE, consume=2) assert limiter_a != limiter assert limiter_a != limiter_b != limiter_c assert limiter_a != limiter_b assert limiter_a.limiter is limiter.limiter assert limiter_a.limiter is not limiter_b.limiter assert limiter_a.attrs == limiter_b.attrs == limiter_c.attrs ``` The only things that are equivalent between the three new limiters above are the limiters' attributes, like the `rate`, `capacity`, and `consume` attributes. ### Creating anonymous, or single-use, limiters You don't have to assign `Limiter` objects to variables. Anonymous limiters don't share a token bucket like named limiters can. They work well when you don't have a reason to share a limiter between two or more blocks of code, and when a limiter has a single or independent purpose. `limiter`, after version `v0.3.0`, ships with a `limit` type alias for `Limiter`: ```python3 from limiter import limit @limit(capacity=2, consume=2) async def send_message(): ... async def upload_image(): async with limit(capacity=3) as limiter: ... ``` The above is equivalent to the below: ```python3 from limiter import Limiter @Limiter(capacity=2, consume=2) async def send_message(): ... async def upload_image(): async with Limiter(capacity=3) as limiter: ... ``` Both `limit` and `Limiter` are the same object: ```python3 assert limit is Limiter ``` # Installation ## Requirements - Python 3.10+ for versions `0.3.0` and up - [Python 3.7+ for versions below `0.3.0`](https://github.com/alexdelorenzo/limiter/blob/master/README-0.2.0.md) ## Install via PyPI ```bash $ python3 -m pip install limiter ``` # License See [`LICENSE`](/LICENSE). If you'd like to use this project with a different license, please get in touch. %package help Summary: Development documents and examples for limiter Provides: python3-limiter-doc %description help # ⏲️ Easy rate limiting for Python `limiter` makes it easy to add [rate limiting](https://en.wikipedia.org/wiki/Rate_limiting) to Python projects, using a [token bucket](https://en.wikipedia.org/wiki/Token_bucket) algorithm. `limiter` can provide Python projects and scripts with: - Rate limiting thread-safe [decorators](https://www.python.org/dev/peps/pep-0318/) - Rate limiting async decorators - Rate limiting thread-safe [context managers](https://www.python.org/dev/peps/pep-0343/) - Rate limiting [async context managers](https://www.python.org/dev/peps/pep-0492/#asynchronous-context-managers-and-async-with) Here are some features and benefits of using `limiter`: - Easily control burst and average request rates - It is [thread-safe, with no need for a timer thread](https://en.wikipedia.org/wiki/Generic_cell_rate_algorithm#Comparison_with_the_token_bucket) - It adds [jitter](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/) to help with contention - It has a simple API that takes advantage of Python's features, idioms and [type hinting](https://www.python.org/dev/peps/pep-0483/) ## Example Here's an example of using a limiter as a decorator and context manager: ```python from aiohttp import ClientSession from limiter import Limiter limit_downloads = Limiter(rate=2, capacity=5, consume=2) @limit_downloads async def download_image(url: str) -> bytes: async with ClientSession() as session, session.get(url) as response: return await response.read() async def download_page(url: str) -> str: async with ( ClientSession() as session, limit_downloads, session.get(url) as response ): return await response.text() ``` # Usage You can define limiters and use them dynamically across your project. **Note**: If you're using Python version `3.9.x` or below, check out [the documentation for version `0.2.0` of `limiter` here](https://github.com/alexdelorenzo/limiter/blob/master/README-0.2.0.md). ### Limiting blocks of code `limiter` can rate limit all Python callables, and limiters can be used as context managers. You can define a limiter with a set refresh `rate` and total token `capacity`. You can set the amount of tokens to consume dynamically with `consume`, and the `bucket` parameter sets the bucket to consume tokens from: ```python3 from limiter import Limiter REFRESH_RATE: int = 2 BURST_RATE: int = 3 MSG_BUCKET: str = 'messages' limiter: Limiter = Limiter(rate=REFRESH_RATE, capacity=BURST_RATE) limit_msgs: Limiter = limiter(bucket=MSG_BUCKET) @limiter def download_page(url: str) -> bytes: ... @limiter(consume=2) async def download_page(url: str) -> bytes: ... def send_page(page: bytes): with limiter(consume=1.5, bucket=MSG_BUCKET): ... async def send_page(page: bytes): async with limit_msgs: ... @limit_msgs(consume=3) def send_email(to: str): ... async def send_email(to: str): async with limiter(bucket=MSG_BUCKET): ... ``` In the example above, both `limiter` and `limit_msgs` share the same limiter. The only difference is that `limit_msgs` will take tokens from the `MSG_BUCKET` bucket by default. ```python3 assert limiter.limiter is limit_msgs.limiter assert limiter.bucket != limit_msgs.bucket assert limiter != limit_msgs ``` ### Creating new limiters You can reuse existing limiters in your code, and you can create new limiters from the parameters of an existing limiter using the `new()` method. Or, you can define a new limiter entirely: ```python # you can reuse existing limiters limit_downloads: Limiter = limiter(consume=2) # you can use the settings from an existing limiter in a new limiter limit_downloads: Limiter = limiter.new(consume=2) # or you can simply define a new limiter limit_downloads: Limiter = Limiter(REFRESH_RATE, BURST_RATE, consume=2) @limit_downloads def download_page(url: str) -> bytes: ... @limit_downloads async def download_page(url: str) -> bytes: ... def download_image(url: str) -> bytes: with limit_downloads: ... async def download_image(url: str) -> bytes: async with limit_downloads: ... ``` Let's look at the difference between reusing an existing limiter, and creating new limiters with the `new()` method: ```python3 limiter_a: Limiter = limiter(consume=2) limiter_b: Limiter = limiter.new(consume=2) limiter_c: Limiter = Limiter(REFRESH_RATE, BURST_RATE, consume=2) assert limiter_a != limiter assert limiter_a != limiter_b != limiter_c assert limiter_a != limiter_b assert limiter_a.limiter is limiter.limiter assert limiter_a.limiter is not limiter_b.limiter assert limiter_a.attrs == limiter_b.attrs == limiter_c.attrs ``` The only things that are equivalent between the three new limiters above are the limiters' attributes, like the `rate`, `capacity`, and `consume` attributes. ### Creating anonymous, or single-use, limiters You don't have to assign `Limiter` objects to variables. Anonymous limiters don't share a token bucket like named limiters can. They work well when you don't have a reason to share a limiter between two or more blocks of code, and when a limiter has a single or independent purpose. `limiter`, after version `v0.3.0`, ships with a `limit` type alias for `Limiter`: ```python3 from limiter import limit @limit(capacity=2, consume=2) async def send_message(): ... async def upload_image(): async with limit(capacity=3) as limiter: ... ``` The above is equivalent to the below: ```python3 from limiter import Limiter @Limiter(capacity=2, consume=2) async def send_message(): ... async def upload_image(): async with Limiter(capacity=3) as limiter: ... ``` Both `limit` and `Limiter` are the same object: ```python3 assert limit is Limiter ``` # Installation ## Requirements - Python 3.10+ for versions `0.3.0` and up - [Python 3.7+ for versions below `0.3.0`](https://github.com/alexdelorenzo/limiter/blob/master/README-0.2.0.md) ## Install via PyPI ```bash $ python3 -m pip install limiter ``` # License See [`LICENSE`](/LICENSE). If you'd like to use this project with a different license, please get in touch. %prep %autosetup -n limiter-0.3.1 %build %py3_build %install %py3_install install -d -m755 %{buildroot}/%{_pkgdocdir} if [ -d doc ]; then cp -arf doc %{buildroot}/%{_pkgdocdir}; fi if [ -d docs ]; then cp -arf docs %{buildroot}/%{_pkgdocdir}; fi if [ -d example ]; then cp -arf example %{buildroot}/%{_pkgdocdir}; fi if [ -d examples ]; then cp -arf examples %{buildroot}/%{_pkgdocdir}; fi pushd %{buildroot} if [ -d usr/lib ]; then find usr/lib -type f -printf "/%h/%f\n" >> filelist.lst fi if [ -d usr/lib64 ]; then find usr/lib64 -type f -printf "/%h/%f\n" >> filelist.lst fi if [ -d usr/bin ]; then find usr/bin -type f -printf "/%h/%f\n" >> filelist.lst fi if [ -d usr/sbin ]; then find usr/sbin -type f -printf "/%h/%f\n" >> filelist.lst fi touch doclist.lst if [ -d usr/share/man ]; then find usr/share/man -type f -printf "/%h/%f.gz\n" >> doclist.lst fi popd mv %{buildroot}/filelist.lst . mv %{buildroot}/doclist.lst . %files -n python3-limiter -f filelist.lst %dir %{python3_sitelib}/* %files help -f doclist.lst %{_docdir}/* %changelog * Fri May 05 2023 Python_Bot - 0.3.1-1 - Package Spec generated