Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

⏲️ Easy rate limiting for Python using a token bucket algorithm, with async and thread-safe decorators and context managers

License

NotificationsYou must be signed in to change notification settings

alexdelorenzo/limiter

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

limiter makes it easy to addrate limiting to Python projects, usingatoken bucket algorithm.limiter can provide Python projects andscripts with:

Here are some features and benefits of usinglimiter:

Example

Here's an example of using a limiter as a decorator and context manager:

fromaiohttpimportClientSessionfromlimiterimportLimiterlimit_downloads=Limiter(rate=2,capacity=5,consume=2)@limit_downloadsasyncdefdownload_image(url:str)->bytes:asyncwithClientSession()assession,session.get(url)asresponse:returnawaitresponse.read()asyncdefdownload_page(url:str)->str:asyncwith (ClientSession()assession,limit_downloads,session.get(url)asresponse  ):returnawaitresponse.text()

Usage

You can define limiters and use them dynamically across your project.

Note: If you're using Python version3.9.x or below, checkoutthe documentation for version0.2.0 oflimiter here.

Limiter instances

Limiter instances takerate,capacity andconsume arguments.

  • rate is the token replenishment rate per second. Tokens are automatically added every second.
  • consume is the amount of tokens consumed from the token bucket upon successfully taking tokens from the bucket.
  • capacity is the total amount of tokens the token bucket can hold. Token replenishment stops when this capacity isreached.

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 refreshrate and total tokencapacity. You can set the amount of tokens toconsume dynamically withconsume, and thebucket parameter sets the bucket to consume tokens from:

fromlimiterimportLimiterREFRESH_RATE:int=2BURST_RATE:int=3MSG_BUCKET:str='messages'limiter:Limiter=Limiter(rate=REFRESH_RATE,capacity=BURST_RATE)limit_msgs:Limiter=limiter(bucket=MSG_BUCKET)@limiterdefdownload_page(url:str)->bytes:  ...@limiter(consume=2)asyncdefdownload_page(url:str)->bytes:  ...defsend_page(page:bytes):withlimiter(consume=1.5,bucket=MSG_BUCKET):    ...asyncdefsend_page(page:bytes):asyncwithlimit_msgs:    ...@limit_msgs(consume=3)defsend_email(to:str):  ...asyncdefsend_email(to:str):asyncwithlimiter(bucket=MSG_BUCKET):    ...

In the example above, bothlimiter andlimit_msgs share the same limiter. The only difference is thatlimit_msgswill take tokens from theMSG_BUCKET bucket by default.

assertlimiter.limiterislimit_msgs.limiterassertlimiter.bucket!=limit_msgs.bucketassertlimiter!=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 limiterusing thenew() method.

Or, you can define a new limiter entirely:

# you can reuse existing limiterslimit_downloads:Limiter=limiter(consume=2)# you can use the settings from an existing limiter in a new limiterlimit_downloads:Limiter=limiter.new(consume=2)# or you can simply define a new limiterlimit_downloads:Limiter=Limiter(REFRESH_RATE,BURST_RATE,consume=2)@limit_downloadsdefdownload_page(url:str)->bytes:  ...@limit_downloadsasyncdefdownload_page(url:str)->bytes:  ...defdownload_image(url:str)->bytes:withlimit_downloads:    ...asyncdefdownload_image(url:str)->bytes:asyncwithlimit_downloads:    ...

Let's look at the difference between reusing an existing limiter, and creating new limiters with thenew() method:

limiter_a:Limiter=limiter(consume=2)limiter_b:Limiter=limiter.new(consume=2)limiter_c:Limiter=Limiter(REFRESH_RATE,BURST_RATE,consume=2)assertlimiter_a!=limiterassertlimiter_a!=limiter_b!=limiter_cassertlimiter_a!=limiter_bassertlimiter_a.limiterislimiter.limiterassertlimiter_a.limiterisnotlimiter_b.limiterassertlimiter_a.attrs==limiter_b.attrs==limiter_c.attrs

The only things that are equivalent between the three new limiters above are the limiters' attributes, liketherate,capacity, andconsume attributes.

Creating anonymous, or single-use, limiters

You don't have to assignLimiter objects to variables. Anonymous limiters don't share a token bucket like namedlimiters can. They work well when you don't have a reason to share a limiter between two or more blocks of code, andwhen a limiter has a single or independent purpose.

limiter, after versionv0.3.0, ships with alimit type alias forLimiter:

fromlimiterimportlimit@limit(capacity=2,consume=2)asyncdefsend_message():  ...asyncdefupload_image():asyncwithlimit(capacity=3)aslimiter:    ...

The above is equivalent to the below:

fromlimiterimportLimiter@Limiter(capacity=2,consume=2)asyncdefsend_message():  ...asyncdefupload_image():asyncwithLimiter(capacity=3)aslimiter:    ...

Bothlimit andLimiter are the same object:

assertlimitisLimiter

Jitter

ALimiter'sjitter argument adds jitter to help with contention.

The value is inunits, which is milliseconds by default, and can be any of these:

  • False, to add no jitter. This is the default.
  • True, to add a random amount of jitter between0 and50 milliseconds.
  • A number, to add a fixed amount of jitter.
  • Arange object, to add a random amount of jitter within the range.
  • Atuple of two numbers,start andstop, to add a random amount of jitter between the two numbers.
  • Atuple of three numbers:start,stop andstep, to add jitter like you would withrange.

For example, if you want to use a random amount of jitter between0 and100 milliseconds:

limiter=Limiter(rate=2,capacity=5,consume=2,jitter=(0,100))limiter=Limiter(rate=2,capacity=5,consume=2,jitter=(0,100,1))limiter=Limiter(rate=2,capacity=5,consume=2,jitter=range(0,100))limiter=Limiter(rate=2,capacity=5,consume=2,jitter=range(0,100,1))

All of the above are equivalent to each other in function.

You can also supply values forjitter when using decorators or context-managers:

limiter=Limiter(rate=2,capacity=5,consume=2)@limiter(jitter=range(0,100))defdownload_page(url:str)->bytes:  ...asyncdefdownload_page(url:str)->bytes:asyncwithlimiter(jitter=(0,100)):        ...

You can use the above to override default values ofjitter in aLimiter instance.

To add a small amount of random jitter, supplyTrue as the value:

limiter=Limiter(rate=2,capacity=5,consume=2,jitter=True)# or@limiter(jitter=True)defdownload_page(url:str)->bytes:  ...

To turn off jitter in aLimiter configured with jitter, you can supplyFalse as the value:

limiter=Limiter(rate=2,capacity=5,consume=2,jitter=range(10))@limiter(jitter=False)defdownload_page(url:str)->bytes:  ...asyncdefdownload_page(url:str)->bytes:asyncwithlimiter(jitter=False):        ...

Or create a new limiter with jitter turned off:

limiter:Limiter=limiter.new(jitter=False)

Units

units is a number representing the amount of units in one second. The default value is1000 for 1,000 milliseconds in one second.

Similar tojitter,units can be supplied at all the same call sites and constructors thatjitter is accepted.

If you want to use a different unit than milliseconds, supply a different value forunits.

Installation

Requirements

Install via PyPI

$ python3 -m pip install limiter

License

SeeLICENSE. If you'd like to use this project with a different license, please get in touch.

About

⏲️ Easy rate limiting for Python using a token bucket algorithm, with async and thread-safe decorators and context managers

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project

 

Packages

No packages published

Languages


[8]ページ先頭

©2009-2025 Movatter.jp