[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