[Python-modules-commits] [python-ratelimiter] 02/08: New upstream version 1.2.0
chrysn
chrysn-guest at moszumanska.debian.org
Mon Dec 11 08:15:05 UTC 2017
This is an automated email from the git hooks/post-receive script.
chrysn-guest pushed a commit to branch master
in repository python-ratelimiter.
commit 85f13f554cba300b0e97d3119b43f91fb0d9210e
Author: chrysn <chrysn at fsfe.org>
Date: Sun Dec 10 15:11:35 2017 +0100
New upstream version 1.2.0
---
PKG-INFO | 138 ++++++++++++++++++++++++++++++
README | 126 +++++++++++++++++++++++++++
debian/changelog | 5 --
debian/watch | 3 -
ratelimiter.egg-info/PKG-INFO | 138 ++++++++++++++++++++++++++++++
ratelimiter.egg-info/SOURCES.txt | 9 ++
ratelimiter.egg-info/dependency_links.txt | 1 +
ratelimiter.egg-info/requires.txt | 6 ++
ratelimiter.egg-info/top_level.txt | 1 +
ratelimiter.py | 131 ++++++++++++++++++++++++++++
setup.cfg | 5 ++
setup.py | 53 ++++++++++++
tests/__init__.py | 0
13 files changed, 608 insertions(+), 8 deletions(-)
diff --git a/PKG-INFO b/PKG-INFO
new file mode 100644
index 0000000..5c8fe4e
--- /dev/null
+++ b/PKG-INFO
@@ -0,0 +1,138 @@
+Metadata-Version: 1.1
+Name: ratelimiter
+Version: 1.2.0
+Summary: Simple python rate limiting object
+Home-page: https://github.com/RazerM/ratelimiter
+Author: Frazer McLean
+Author-email: frazer at frazermclean.co.uk
+License: Apache
+Description: RateLimiter
+ ===========
+
+ |PyPI Version| |Build Status| |Python Version| |License|
+
+ Simple Python module providing rate limiting.
+
+ Overview
+ --------
+
+ This package provides the ``ratelimiter`` module, which ensures that an
+ operation will not be executed more than a given number of times on a
+ given period. This can prove useful when working with third parties APIs
+ which require for example a maximum of 10 requests per second.
+
+ Usage
+ -----
+
+ Decorator
+ ~~~~~~~~~
+
+ .. code:: python
+
+ from ratelimiter import RateLimiter
+
+ @RateLimiter(max_calls=10, period=1)
+ def do_something():
+ pass
+
+ Context Manager
+ ~~~~~~~~~~~~~~~
+
+ .. code:: python
+
+ from ratelimiter import RateLimiter
+
+ rate_limiter = RateLimiter(max_calls=10, period=1)
+
+ for i in range(100):
+ with rate_limiter:
+ do_something()
+
+ Callback
+ ~~~~~~~~
+
+ The callback is called in its own thread, so your callback may use
+ ``sleep`` without delaying the rate limiter.
+
+ .. code:: python
+
+ import time
+
+ from ratelimiter import RateLimiter
+
+ def limited(until):
+ duration = int(round(until - time.time()))
+ print('Rate limited, sleeping for {:d} seconds'.format(duration))
+
+ rate_limiter = RateLimiter(max_calls=2, period=3, callback=limited)
+
+ for i in range(3):
+ with rate_limiter:
+ print('Iteration', i)
+
+ Output:
+
+ ::
+
+ Iteration 0
+ Iteration 1
+ Rate limited, sleeping for 3 seconds
+ Iteration 2
+
+ asyncio
+ ~~~~~~~
+
+ The ``RateLimiter`` object can be used in an ``async with`` statement on
+ Python 3.5+. Note that the callback must be a coroutine in this context.
+ The coroutine callback is not called in a separate thread.
+
+ .. code:: python
+
+ import asyncio
+ import time
+
+ from ratelimiter import RateLimiter
+
+ async def limited(until):
+ duration = int(round(until - time.time()))
+ print('Rate limited, sleeping for {:d} seconds'.format(duration))
+
+ async def coro():
+ rate_limiter = RateLimiter(max_calls=2, period=3, callback=limited)
+ for i in range(3):
+ async with rate_limiter:
+ print('Iteration', i)
+
+ loop = asyncio.get_event_loop()
+ loop.run_until_complete(coro())
+
+ License
+ -------
+
+ | Original work Copyright 2013 Arnaud Porterie
+ | Modified work Copyright 2016 Frazer McLean
+
+ Licensed under the Apache License, Version 2.0 (the "License"); you may
+ not use this file except in compliance with the License. You may obtain
+ a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ .. |PyPI Version| image:: http://img.shields.io/pypi/v/ratelimiter.svg?style=flat-square
+ :target: https://pypi.python.org/pypi/ratelimiter
+ .. |Build Status| image:: http://img.shields.io/travis/RazerM/ratelimiter/master.svg?style=flat-square
+ :target: https://travis-ci.org/RazerM/ratelimiter
+ .. |Python Version| image:: https://img.shields.io/badge/python-2.7%2C%203-brightgreen.svg?style=flat-square
+ :target: https://www.python.org/downloads/
+ .. |License| image:: http://img.shields.io/badge/license-Apache-blue.svg?style=flat-square
+ :target: https://github.com/RazerM/ratelimiter/blob/master/LICENSE
+
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: License :: OSI Approved :: Apache Software License
diff --git a/README b/README
new file mode 100644
index 0000000..00b8811
--- /dev/null
+++ b/README
@@ -0,0 +1,126 @@
+RateLimiter
+===========
+
+|PyPI Version| |Build Status| |Python Version| |License|
+
+Simple Python module providing rate limiting.
+
+Overview
+--------
+
+This package provides the ``ratelimiter`` module, which ensures that an
+operation will not be executed more than a given number of times on a
+given period. This can prove useful when working with third parties APIs
+which require for example a maximum of 10 requests per second.
+
+Usage
+-----
+
+Decorator
+~~~~~~~~~
+
+.. code:: python
+
+ from ratelimiter import RateLimiter
+
+ @RateLimiter(max_calls=10, period=1)
+ def do_something():
+ pass
+
+Context Manager
+~~~~~~~~~~~~~~~
+
+.. code:: python
+
+ from ratelimiter import RateLimiter
+
+ rate_limiter = RateLimiter(max_calls=10, period=1)
+
+ for i in range(100):
+ with rate_limiter:
+ do_something()
+
+Callback
+~~~~~~~~
+
+The callback is called in its own thread, so your callback may use
+``sleep`` without delaying the rate limiter.
+
+.. code:: python
+
+ import time
+
+ from ratelimiter import RateLimiter
+
+ def limited(until):
+ duration = int(round(until - time.time()))
+ print('Rate limited, sleeping for {:d} seconds'.format(duration))
+
+ rate_limiter = RateLimiter(max_calls=2, period=3, callback=limited)
+
+ for i in range(3):
+ with rate_limiter:
+ print('Iteration', i)
+
+Output:
+
+::
+
+ Iteration 0
+ Iteration 1
+ Rate limited, sleeping for 3 seconds
+ Iteration 2
+
+asyncio
+~~~~~~~
+
+The ``RateLimiter`` object can be used in an ``async with`` statement on
+Python 3.5+. Note that the callback must be a coroutine in this context.
+The coroutine callback is not called in a separate thread.
+
+.. code:: python
+
+ import asyncio
+ import time
+
+ from ratelimiter import RateLimiter
+
+ async def limited(until):
+ duration = int(round(until - time.time()))
+ print('Rate limited, sleeping for {:d} seconds'.format(duration))
+
+ async def coro():
+ rate_limiter = RateLimiter(max_calls=2, period=3, callback=limited)
+ for i in range(3):
+ async with rate_limiter:
+ print('Iteration', i)
+
+ loop = asyncio.get_event_loop()
+ loop.run_until_complete(coro())
+
+License
+-------
+
+| Original work Copyright 2013 Arnaud Porterie
+| Modified work Copyright 2016 Frazer McLean
+
+Licensed under the Apache License, Version 2.0 (the "License"); you may
+not use this file except in compliance with the License. You may obtain
+a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+.. |PyPI Version| image:: http://img.shields.io/pypi/v/ratelimiter.svg?style=flat-square
+ :target: https://pypi.python.org/pypi/ratelimiter
+.. |Build Status| image:: http://img.shields.io/travis/RazerM/ratelimiter/master.svg?style=flat-square
+ :target: https://travis-ci.org/RazerM/ratelimiter
+.. |Python Version| image:: https://img.shields.io/badge/python-2.7%2C%203-brightgreen.svg?style=flat-square
+ :target: https://www.python.org/downloads/
+.. |License| image:: http://img.shields.io/badge/license-Apache-blue.svg?style=flat-square
+ :target: https://github.com/RazerM/ratelimiter/blob/master/LICENSE
diff --git a/debian/changelog b/debian/changelog
deleted file mode 100644
index 744ebe2..0000000
--- a/debian/changelog
+++ /dev/null
@@ -1,5 +0,0 @@
-ratelimiter (0) UNRELEASED; urgency=medium
-
- * Initial release. (Closes: #880661)
-
- -- Christian M. Amsüss <chrysn at fsfe.org> Sun, 10 Dec 2017 15:08:28 +0100
diff --git a/debian/watch b/debian/watch
deleted file mode 100644
index 8f1a1df..0000000
--- a/debian/watch
+++ /dev/null
@@ -1,3 +0,0 @@
-version=3
-opts=uversionmangle=s/(rc|a|b|c)/~$1/ \
-https://pypi.debian.net/ratelimiter/ratelimiter-(.+)\.(?:zip|tgz|tbz|txz|(?:tar\.(?:gz|bz2|xz)))
diff --git a/ratelimiter.egg-info/PKG-INFO b/ratelimiter.egg-info/PKG-INFO
new file mode 100644
index 0000000..5c8fe4e
--- /dev/null
+++ b/ratelimiter.egg-info/PKG-INFO
@@ -0,0 +1,138 @@
+Metadata-Version: 1.1
+Name: ratelimiter
+Version: 1.2.0
+Summary: Simple python rate limiting object
+Home-page: https://github.com/RazerM/ratelimiter
+Author: Frazer McLean
+Author-email: frazer at frazermclean.co.uk
+License: Apache
+Description: RateLimiter
+ ===========
+
+ |PyPI Version| |Build Status| |Python Version| |License|
+
+ Simple Python module providing rate limiting.
+
+ Overview
+ --------
+
+ This package provides the ``ratelimiter`` module, which ensures that an
+ operation will not be executed more than a given number of times on a
+ given period. This can prove useful when working with third parties APIs
+ which require for example a maximum of 10 requests per second.
+
+ Usage
+ -----
+
+ Decorator
+ ~~~~~~~~~
+
+ .. code:: python
+
+ from ratelimiter import RateLimiter
+
+ @RateLimiter(max_calls=10, period=1)
+ def do_something():
+ pass
+
+ Context Manager
+ ~~~~~~~~~~~~~~~
+
+ .. code:: python
+
+ from ratelimiter import RateLimiter
+
+ rate_limiter = RateLimiter(max_calls=10, period=1)
+
+ for i in range(100):
+ with rate_limiter:
+ do_something()
+
+ Callback
+ ~~~~~~~~
+
+ The callback is called in its own thread, so your callback may use
+ ``sleep`` without delaying the rate limiter.
+
+ .. code:: python
+
+ import time
+
+ from ratelimiter import RateLimiter
+
+ def limited(until):
+ duration = int(round(until - time.time()))
+ print('Rate limited, sleeping for {:d} seconds'.format(duration))
+
+ rate_limiter = RateLimiter(max_calls=2, period=3, callback=limited)
+
+ for i in range(3):
+ with rate_limiter:
+ print('Iteration', i)
+
+ Output:
+
+ ::
+
+ Iteration 0
+ Iteration 1
+ Rate limited, sleeping for 3 seconds
+ Iteration 2
+
+ asyncio
+ ~~~~~~~
+
+ The ``RateLimiter`` object can be used in an ``async with`` statement on
+ Python 3.5+. Note that the callback must be a coroutine in this context.
+ The coroutine callback is not called in a separate thread.
+
+ .. code:: python
+
+ import asyncio
+ import time
+
+ from ratelimiter import RateLimiter
+
+ async def limited(until):
+ duration = int(round(until - time.time()))
+ print('Rate limited, sleeping for {:d} seconds'.format(duration))
+
+ async def coro():
+ rate_limiter = RateLimiter(max_calls=2, period=3, callback=limited)
+ for i in range(3):
+ async with rate_limiter:
+ print('Iteration', i)
+
+ loop = asyncio.get_event_loop()
+ loop.run_until_complete(coro())
+
+ License
+ -------
+
+ | Original work Copyright 2013 Arnaud Porterie
+ | Modified work Copyright 2016 Frazer McLean
+
+ Licensed under the Apache License, Version 2.0 (the "License"); you may
+ not use this file except in compliance with the License. You may obtain
+ a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ .. |PyPI Version| image:: http://img.shields.io/pypi/v/ratelimiter.svg?style=flat-square
+ :target: https://pypi.python.org/pypi/ratelimiter
+ .. |Build Status| image:: http://img.shields.io/travis/RazerM/ratelimiter/master.svg?style=flat-square
+ :target: https://travis-ci.org/RazerM/ratelimiter
+ .. |Python Version| image:: https://img.shields.io/badge/python-2.7%2C%203-brightgreen.svg?style=flat-square
+ :target: https://www.python.org/downloads/
+ .. |License| image:: http://img.shields.io/badge/license-Apache-blue.svg?style=flat-square
+ :target: https://github.com/RazerM/ratelimiter/blob/master/LICENSE
+
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: License :: OSI Approved :: Apache Software License
diff --git a/ratelimiter.egg-info/SOURCES.txt b/ratelimiter.egg-info/SOURCES.txt
new file mode 100644
index 0000000..7d3709d
--- /dev/null
+++ b/ratelimiter.egg-info/SOURCES.txt
@@ -0,0 +1,9 @@
+README
+ratelimiter.py
+setup.py
+ratelimiter.egg-info/PKG-INFO
+ratelimiter.egg-info/SOURCES.txt
+ratelimiter.egg-info/dependency_links.txt
+ratelimiter.egg-info/requires.txt
+ratelimiter.egg-info/top_level.txt
+tests/__init__.py
\ No newline at end of file
diff --git a/ratelimiter.egg-info/dependency_links.txt b/ratelimiter.egg-info/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/ratelimiter.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/ratelimiter.egg-info/requires.txt b/ratelimiter.egg-info/requires.txt
new file mode 100644
index 0000000..01bd58d
--- /dev/null
+++ b/ratelimiter.egg-info/requires.txt
@@ -0,0 +1,6 @@
+
+[test]
+pytest>=3.0
+
+[test:python_version>="3.5"]
+pytest-asyncio
diff --git a/ratelimiter.egg-info/top_level.txt b/ratelimiter.egg-info/top_level.txt
new file mode 100644
index 0000000..f28cf28
--- /dev/null
+++ b/ratelimiter.egg-info/top_level.txt
@@ -0,0 +1 @@
+ratelimiter
diff --git a/ratelimiter.py b/ratelimiter.py
new file mode 100644
index 0000000..6df2673
--- /dev/null
+++ b/ratelimiter.py
@@ -0,0 +1,131 @@
+# Original work Copyright 2013 Arnaud Porterie
+# Modified work Copyright 2016 Frazer McLean
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import collections
+import functools
+import threading
+import time
+import sys
+from textwrap import dedent
+
+try:
+ import asyncio
+except ImportError:
+ asyncio = None
+
+__author__ = 'Frazer McLean <frazer at frazermclean.co.uk>'
+__version__ = '1.2.0'
+__license__ = 'Apache'
+__description__ = 'Simple python rate limiting object'
+
+PY35 = sys.version_info >= (3, 5)
+
+
+class RateLimiter(object):
+ """Provides rate limiting for an operation with a configurable number of
+ requests for a time period.
+ """
+
+ def __init__(self, max_calls, period=1.0, callback=None):
+ """Initialize a RateLimiter object which enforces as much as max_calls
+ operations on period (eventually floating) number of seconds.
+ """
+ if period <= 0:
+ raise ValueError('Rate limiting period should be > 0')
+ if max_calls <= 0:
+ raise ValueError('Rate limiting number of calls should be > 0')
+
+ # We're using a deque to store the last execution timestamps, not for
+ # its maxlen attribute, but to allow constant time front removal.
+ self.calls = collections.deque()
+
+ self.period = period
+ self.max_calls = max_calls
+ self.callback = callback
+ self._lock = threading.Lock()
+ self._alock = None
+
+ # Lock to protect creation of self._alock
+ self._init_lock = threading.Lock()
+
+ def _init_async_lock(self):
+ with self._init_lock:
+ if self._alock is None:
+ self._alock = asyncio.Lock()
+
+ def __call__(self, f):
+ """The __call__ function allows the RateLimiter object to be used as a
+ regular function decorator.
+ """
+ @functools.wraps(f)
+ def wrapped(*args, **kwargs):
+ with self:
+ return f(*args, **kwargs)
+ return wrapped
+
+ def __enter__(self):
+ with self._lock:
+ # We want to ensure that no more than max_calls were run in the allowed
+ # period. For this, we store the last timestamps of each call and run
+ # the rate verification upon each __enter__ call.
+ if len(self.calls) >= self.max_calls:
+ until = time.time() + self.period - self._timespan
+ if self.callback:
+ t = threading.Thread(target=self.callback, args=(until,))
+ t.daemon = True
+ t.start()
+ sleeptime = until - time.time()
+ if sleeptime > 0:
+ time.sleep(sleeptime)
+ return self
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ with self._lock:
+ # Store the last operation timestamp.
+ self.calls.append(time.time())
+
+ # Pop the timestamp list front (ie: the older calls) until the sum goes
+ # back below the period. This is our 'sliding period' window.
+ while self._timespan >= self.period:
+ self.calls.popleft()
+
+ if PY35:
+ # We have to exec this due to syntax errors on earlier versions.
+ aenter_code = dedent("""
+ async def __aenter__(self):
+ if self._alock is None:
+ self._init_async_lock()
+
+ with await self._alock:
+ # We want to ensure that no more than max_calls were run in the allowed
+ # period. For this, we store the last timestamps of each call and run
+ # the rate verification upon each __enter__ call.
+ if len(self.calls) >= self.max_calls:
+ until = time.time() + self.period - self._timespan
+ if self.callback:
+ asyncio.ensure_future(self.callback(until))
+ sleeptime = until - time.time()
+ if sleeptime > 0:
+ await asyncio.sleep(sleeptime)
+ return self
+
+ """)
+ exec(aenter_code)
+
+ __aexit__ = asyncio.coroutine(__exit__)
+
+ @property
+ def _timespan(self):
+ return self.calls[-1] - self.calls[0]
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..861a9f5
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,5 @@
+[egg_info]
+tag_build =
+tag_date = 0
+tag_svn_revision = 0
+
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..42f24c6
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,53 @@
+# Original work Copyright 2013 Arnaud Porterie
+# Modified work Copyright 2015 Frazer McLean
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from setuptools import setup
+import re
+
+FILE = 'ratelimiter.py'
+init_data = open(FILE).read()
+
+metadata = dict(re.findall("__([a-z]+)__ = '([^']+)'", init_data))
+
+AUTHOR_EMAIL = metadata['author']
+VERSION = metadata['version']
+LICENSE = metadata['license']
+DESCRIPTION = metadata['description']
+
+AUTHOR, EMAIL = re.match(r'(.*) <(.*)>', AUTHOR_EMAIL).groups()
+
+extras_require = dict()
+
+extras_require['test'] = {
+ 'pytest>=3.0',
+}
+
+extras_require['test:python_version>="3.5"'] = {'pytest-asyncio'}
+
+setup(
+ name='ratelimiter',
+ version=VERSION,
+ description=DESCRIPTION,
+ long_description=open('README').read(),
+ author=AUTHOR,
+ author_email=EMAIL,
+ url='https://github.com/RazerM/ratelimiter',
+ py_modules=['ratelimiter'],
+ classifiers=[
+ 'Development Status :: 5 - Production/Stable',
+ 'License :: OSI Approved :: Apache Software License',
+ ],
+ license=LICENSE,
+ extras_require=extras_require)
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..e69de29
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-ratelimiter.git
More information about the Python-modules-commits
mailing list