[Python-modules-commits] [api-hour] 01/04: Import api-hour_0.8.2.orig.tar.gz

Piotr Ożarowski piotr at moszumanska.debian.org
Mon Nov 13 20:21:52 UTC 2017


This is an automated email from the git hooks/post-receive script.

piotr pushed a commit to branch master
in repository api-hour.

commit e391298a2bcb50541a568f646e07ae0bd956c287
Author: Piotr Ożarowski <piotr at debian.org>
Date:   Mon Nov 13 21:03:08 2017 +0100

    Import api-hour_0.8.2.orig.tar.gz
---
 HISTORY.rst                                        |  6 ++
 PKG-INFO                                           | 20 +++--
 README.rst                                         | 12 +--
 api_hour.egg-info/PKG-INFO                         | 20 +++--
 api_hour.egg-info/SOURCES.txt                      |  3 +
 api_hour/__init__.py                               |  2 +-
 api_hour/container.py                              | 14 +++-
 api_hour/plugins/aiohttp/__init__.py               | 32 +------
 api_hour/plugins/aiohttp/environment.py            | 98 ++++++++++++++++++++++
 .../plugins/aiohttp/{__init__.py => response.py}   | 15 ++--
 api_hour/plugins/aiohttp/router.py                 | 16 ++++
 api_hour/worker.py                                 | 40 +++++----
 setup.cfg                                          |  2 +-
 setup.py                                           |  6 +-
 14 files changed, 206 insertions(+), 80 deletions(-)

diff --git a/HISTORY.rst b/HISTORY.rst
index c2d2d7c..ec69a46 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -1,6 +1,12 @@
 CHANGES
 =======
 
+0.8.2 (2017-11-10)
+------------------
+
+* Add pre_start coroutine
+* Fix setup.py to check correctly minimal Python version. Thanks @romuald
+
 0.8.1 (2016-07-08)
 ------------------
 
diff --git a/PKG-INFO b/PKG-INFO
index ea00f73..263bae5 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: api_hour
-Version: 0.8.1
+Version: 0.8.2
 Summary: Write efficient network daemons (HTTP, SSH...) with ease.
 Home-page: http://www.api-hour.io
 Author: Eyepea Dev Team
@@ -45,37 +45,37 @@ Description: API Hour
         For each layer, we use the best in term of performance and simplicity:
         
         #. `AsyncIO <https://docs.python.org/3/library/asyncio.html>`_: an easy asynchronous framework, directly integrated in Python 3.4+
-        #. `aiohttp.web <http://aiohttp.readthedocs.org/en/latest/web.html>`_: HTTP protocol implementation for AsyncIO + Web framework
+        #. `aiohttp.web <https://aiohttp.readthedocs.org/en/latest/web.html>`_: HTTP protocol implementation for AsyncIO + Web framework
         #. `ujson <https://github.com/esnme/ultrajson#ultrajson>`_: fastest JSON serialization
         
         Examples
         --------
         
         #. `API-Hour Starter Kit (Cookiecutter) <https://github.com/Eyepea/cookiecutter-API-Hour>`_
-        #. `API-Hour implementation of TechEmpower Web Framework Benchmarks <https://github.com/Eyepea/FrameworkBenchmarks/tree/API-Hour/frameworks/Python/API-Hour>`_
+        #. `API-Hour implementation of TechEmpower Web Framework Benchmarks <https://github.com/TechEmpower/FrameworkBenchmarks/tree/master/frameworks/Python/asyncio>`_
         #. `HTTP+SSH Daemon <https://github.com/Eyepea/API-Hour/tree/master/examples/http_and_ssh>`_
         #. `Quick'n'dirty benchmarks on a kitchen table <https://github.com/Eyepea/API-Hour/tree/master/benchmarks/api_hour/benchmarks>`_
         
         How-to start an API-Hour project ?
         ----------------------------------
         
-        You can follow `one of our tutorials <http://pythonhosted.org/api_hour/tutorials/index.html>`_
+        You can follow `one of our tutorials <https://pythonhosted.org/api_hour/tutorials/index.html>`_
         
         Support
         -------
         
-        * `Documentation <http://pythonhosted.org/api_hour/>`_.
+        * `Documentation <https://pythonhosted.org/api_hour/>`_.
         * `Mailing-list <https://groups.google.com/d/forum/api-hour>`_
         
         Requirements
         ------------
         
-        - Python 3.3+
+        - Python 3.5+
         
         Install
         -------
         
-        Follow `official documentation <http://pythonhosted.org/api_hour/installation.html>`_.
+        Follow `official documentation <https://pythonhosted.org/api_hour/installation.html>`_.
         
         License
         -------
@@ -114,6 +114,12 @@ Description: API Hour
         CHANGES
         =======
         
+        0.8.2 (2017-11-10)
+        ------------------
+        
+        * Add pre_start coroutine
+        * Fix setup.py to check correctly minimal Python version. Thanks @romuald
+        
         0.8.1 (2016-07-08)
         ------------------
         
diff --git a/README.rst b/README.rst
index 0f5cbe1..3dc11c4 100644
--- a/README.rst
+++ b/README.rst
@@ -36,37 +36,37 @@ Moreover, we've tried to reduce as much as possible layers between your code and
 For each layer, we use the best in term of performance and simplicity:
 
 #. `AsyncIO <https://docs.python.org/3/library/asyncio.html>`_: an easy asynchronous framework, directly integrated in Python 3.4+
-#. `aiohttp.web <http://aiohttp.readthedocs.org/en/latest/web.html>`_: HTTP protocol implementation for AsyncIO + Web framework
+#. `aiohttp.web <https://aiohttp.readthedocs.org/en/latest/web.html>`_: HTTP protocol implementation for AsyncIO + Web framework
 #. `ujson <https://github.com/esnme/ultrajson#ultrajson>`_: fastest JSON serialization
 
 Examples
 --------
 
 #. `API-Hour Starter Kit (Cookiecutter) <https://github.com/Eyepea/cookiecutter-API-Hour>`_
-#. `API-Hour implementation of TechEmpower Web Framework Benchmarks <https://github.com/Eyepea/FrameworkBenchmarks/tree/API-Hour/frameworks/Python/API-Hour>`_
+#. `API-Hour implementation of TechEmpower Web Framework Benchmarks <https://github.com/TechEmpower/FrameworkBenchmarks/tree/master/frameworks/Python/asyncio>`_
 #. `HTTP+SSH Daemon <https://github.com/Eyepea/API-Hour/tree/master/examples/http_and_ssh>`_
 #. `Quick'n'dirty benchmarks on a kitchen table <https://github.com/Eyepea/API-Hour/tree/master/benchmarks/api_hour/benchmarks>`_
 
 How-to start an API-Hour project ?
 ----------------------------------
 
-You can follow `one of our tutorials <http://pythonhosted.org/api_hour/tutorials/index.html>`_
+You can follow `one of our tutorials <https://pythonhosted.org/api_hour/tutorials/index.html>`_
 
 Support
 -------
 
-* `Documentation <http://pythonhosted.org/api_hour/>`_.
+* `Documentation <https://pythonhosted.org/api_hour/>`_.
 * `Mailing-list <https://groups.google.com/d/forum/api-hour>`_
 
 Requirements
 ------------
 
-- Python 3.3+
+- Python 3.5+
 
 Install
 -------
 
-Follow `official documentation <http://pythonhosted.org/api_hour/installation.html>`_.
+Follow `official documentation <https://pythonhosted.org/api_hour/installation.html>`_.
 
 License
 -------
diff --git a/api_hour.egg-info/PKG-INFO b/api_hour.egg-info/PKG-INFO
index 527f1c4..d73ea12 100644
--- a/api_hour.egg-info/PKG-INFO
+++ b/api_hour.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: api-hour
-Version: 0.8.1
+Version: 0.8.2
 Summary: Write efficient network daemons (HTTP, SSH...) with ease.
 Home-page: http://www.api-hour.io
 Author: Eyepea Dev Team
@@ -45,37 +45,37 @@ Description: API Hour
         For each layer, we use the best in term of performance and simplicity:
         
         #. `AsyncIO <https://docs.python.org/3/library/asyncio.html>`_: an easy asynchronous framework, directly integrated in Python 3.4+
-        #. `aiohttp.web <http://aiohttp.readthedocs.org/en/latest/web.html>`_: HTTP protocol implementation for AsyncIO + Web framework
+        #. `aiohttp.web <https://aiohttp.readthedocs.org/en/latest/web.html>`_: HTTP protocol implementation for AsyncIO + Web framework
         #. `ujson <https://github.com/esnme/ultrajson#ultrajson>`_: fastest JSON serialization
         
         Examples
         --------
         
         #. `API-Hour Starter Kit (Cookiecutter) <https://github.com/Eyepea/cookiecutter-API-Hour>`_
-        #. `API-Hour implementation of TechEmpower Web Framework Benchmarks <https://github.com/Eyepea/FrameworkBenchmarks/tree/API-Hour/frameworks/Python/API-Hour>`_
+        #. `API-Hour implementation of TechEmpower Web Framework Benchmarks <https://github.com/TechEmpower/FrameworkBenchmarks/tree/master/frameworks/Python/asyncio>`_
         #. `HTTP+SSH Daemon <https://github.com/Eyepea/API-Hour/tree/master/examples/http_and_ssh>`_
         #. `Quick'n'dirty benchmarks on a kitchen table <https://github.com/Eyepea/API-Hour/tree/master/benchmarks/api_hour/benchmarks>`_
         
         How-to start an API-Hour project ?
         ----------------------------------
         
-        You can follow `one of our tutorials <http://pythonhosted.org/api_hour/tutorials/index.html>`_
+        You can follow `one of our tutorials <https://pythonhosted.org/api_hour/tutorials/index.html>`_
         
         Support
         -------
         
-        * `Documentation <http://pythonhosted.org/api_hour/>`_.
+        * `Documentation <https://pythonhosted.org/api_hour/>`_.
         * `Mailing-list <https://groups.google.com/d/forum/api-hour>`_
         
         Requirements
         ------------
         
-        - Python 3.3+
+        - Python 3.5+
         
         Install
         -------
         
-        Follow `official documentation <http://pythonhosted.org/api_hour/installation.html>`_.
+        Follow `official documentation <https://pythonhosted.org/api_hour/installation.html>`_.
         
         License
         -------
@@ -114,6 +114,12 @@ Description: API Hour
         CHANGES
         =======
         
+        0.8.2 (2017-11-10)
+        ------------------
+        
+        * Add pre_start coroutine
+        * Fix setup.py to check correctly minimal Python version. Thanks @romuald
+        
         0.8.1 (2016-07-08)
         ------------------
         
diff --git a/api_hour.egg-info/SOURCES.txt b/api_hour.egg-info/SOURCES.txt
index 9a9ccfc..fe959a8 100644
--- a/api_hour.egg-info/SOURCES.txt
+++ b/api_hour.egg-info/SOURCES.txt
@@ -18,6 +18,9 @@ api_hour.egg-info/requires.txt
 api_hour.egg-info/top_level.txt
 api_hour/plugins/__init__.py
 api_hour/plugins/aiohttp/__init__.py
+api_hour/plugins/aiohttp/environment.py
+api_hour/plugins/aiohttp/response.py
+api_hour/plugins/aiohttp/router.py
 test/__init__.py
 test/plugins/__init__.py
 test/plugins/aiohttp/__init__.py
diff --git a/api_hour/__init__.py b/api_hour/__init__.py
index 8d75736..a7bb360 100644
--- a/api_hour/__init__.py
+++ b/api_hour/__init__.py
@@ -4,7 +4,7 @@ import collections
 
 __author__ = 'Ludovic Gasc (GMLudo)'
 __email__ = 'git at gmludo.eu'
-__version__ = '0.8.1'
+__version__ = '0.8.2'
 version = __version__ + ' , Python ' + sys.version
 
 
diff --git a/api_hour/container.py b/api_hour/container.py
index 7598780..ef9281d 100644
--- a/api_hour/container.py
+++ b/api_hour/container.py
@@ -37,19 +37,25 @@ class Container:
         """To customize loop generation"""
         return asyncio.new_event_loop()
 
+    async def pre_start(self):
+        pass
+
     async def start(self):
         LOG.info('Starting application...')
 
     def pre_stop(self):
         if not self._stopping:
             self._stopping = True
-            task = asyncio.ensure_future(self.stop(), loop=self.loop)
-            task.add_done_callback(self.post_stop)
+            task = asyncio.ensure_future(self.shutdown(), loop=self.loop)
+            task.add_done_callback(self.cleanup())
         else:
             LOG.debug('Already stopping application, not doing anything')
 
-    async def stop(self):
-        LOG.info('Stopping application...')
+    async def shutdown(self):
+        pass
+
+    async def cleanup(self):
+        pass
 
     def post_stop(self, future):
         pass
diff --git a/api_hour/plugins/aiohttp/__init__.py b/api_hour/plugins/aiohttp/__init__.py
index 4c82405..107aa54 100644
--- a/api_hour/plugins/aiohttp/__init__.py
+++ b/api_hour/plugins/aiohttp/__init__.py
@@ -1,29 +1,3 @@
-from aiohttp.web import Response
-
-try:
-    import ujson as json
-except ImportError:
-    import json
-
-
-class JSON(Response):
-    """Serialize response to JSON with aiohttp.web"""
-
-    def __init__(self, data, status=200,
-                 reason=None, headers=None):
-        body = json.dumps(data).encode('utf-8')
-
-        super().__init__(body=body, status=status, reason=reason,
-                         headers=headers, content_type='application/json')
-
-
-class HTML(Response):
-    """Serialize response to HTML with aiohttp.web"""
-
-    def __init__(self, data, status=200,
-                 reason=None, headers=None):
-
-        body = data.encode('utf-8')
-
-        super().__init__(body=body, status=status, reason=reason,
-                         headers=headers, content_type='text/html')
\ No newline at end of file
+from .response import JSON, HTML
+from .environment import env_middleware_factory
+from .router import Router
diff --git a/api_hour/plugins/aiohttp/environment.py b/api_hour/plugins/aiohttp/environment.py
new file mode 100644
index 0000000..b494a0b
--- /dev/null
+++ b/api_hour/plugins/aiohttp/environment.py
@@ -0,0 +1,98 @@
+import logging
+
+try:
+    import ujson as json
+except ImportError:
+    import json
+
+from . import JSON
+
+LOG = logging.getLogger(__name__)
+
+
+async def env_middleware_factory(app, handler):
+    async def env_middleware(request):
+
+        LOG.debug('incoming request %s from: %s', request.method, request.path)
+        request['env'] = {
+            'container': request.app['ah_container'],
+            'id': request.headers.get('Request_ID', None)
+        }
+
+        try:
+            name = request.match_info.route.resource.name
+            config = request['env']['container'].env_config.get(name, [])
+        except AttributeError:
+            LOG.debug('Can not retrieve resource name for %(http_path)s', {'http_path': request.path})
+            return await handler(request)
+
+        LOG.debug('Resource name: %s, config: %s', name, config)
+
+        if 'pg' in config and 'pg' in request['env']['container'].engines:
+            LOG.debug('Creating pg cursor')
+            request['env']['pg'] = dict()
+            request['env']['pg']['engine'] = await request['env']['container'].engines['pg']
+            request['env']['pg']['cursor_context_manager'] = await request['env']['pg']['engine'].cursor()
+            request['env']['pg']['cursor'] = request['env']['pg']['cursor_context_manager'].__enter__()
+            await request['env']['pg']['cursor'].execute('BEGIN')
+
+        if 'mysql' in config and 'mysql' in request['env']['container'].engines:
+            LOG.debug('Creating mysql cursor')
+            request['env']['mysql'] = dict()
+            request['env']['mysql']['engine'] = await request['env']['container'].engines['mysql']
+            request['env']['mysql']['connection'] = await request['env']['mysql']['engine'].acquire()
+            request['env']['mysql']['cursor'] = await request['env']['mysql']['connection'].cursor()
+            await request['env']['mysql']['connection'].begin()
+
+        if 'redis' in config and 'redis' in request['env']['container'].engines:
+            LOG.debug('Creating redis connection')
+            request['env']['redis'] = dict()
+            request['env']['redis']['engine'] = await request['env']['container'].engines['redis']
+            request['env']['redis']['connection'] = await request['env']['redis']['engine'].acquire()
+
+        if 'json' in config:
+            LOG.debug('Parsing json')
+            try:
+                request['env']['json'] = await request.json(loads=json.loads)
+            except ValueError:
+                raise JSON(status=400, data='''The payload must be of json format''')
+            LOG.debug('Json parsed')
+
+        request['env']['name'] = name
+        request['env']['config'] = config
+
+        try:
+            LOG.debug('Handling request')
+            response = await handler(request)
+        except Exception as exception:
+            if 'pg' in config and 'pg' in request['env'] and not request['env']['pg']['cursor'].closed:
+                LOG.debug('Rolling back postgres transaction')
+                await request['env']['pg']['cursor'].execute('ROLLBACK')
+                request['env']['pg']['cursor_context_manager'].__exit__()
+
+            if 'mysql' in config and 'mysql' in request['env'] and not request['env']['mysql']['cursor'].closed:
+                LOG.debug('Rolling back mysql transaction')
+                await request['env']['mysql']['connection'].rollback()
+                await request['env']['mysql']['cursor'].close()
+                request['env']['mysql']['engine'].release(request['env']['mysql']['connection'])
+            raise exception
+        else:
+            if 'pg' in config and 'pg' in request['env'] and not request['env']['pg']['cursor'].closed:
+                LOG.debug('Committing postgres transaction')
+                await request['env']['pg']['cursor'].execute('COMMIT')
+                request['env']['pg']['cursor_context_manager'].__exit__()
+
+            if 'mysql' in config and 'mysql' in request['env'] and not request['env']['mysql']['cursor'].closed:
+                LOG.debug('Committing mysql transaction')
+                await request['env']['mysql']['connection'].commit()
+                await request['env']['mysql']['cursor'].close()
+                request['env']['mysql']['engine'].release(request['env']['mysql']['connection'])
+        finally:
+            if 'redis' in config and 'redis' in request['env'] and not request['env']['redis']['connection'].closed:
+                LOG.debug('Closing redis connection')
+                request['env']['redis']['connection'].close()
+                await request['env']['redis']['connection'].wait_closed()
+
+        return response
+
+    return env_middleware
diff --git a/api_hour/plugins/aiohttp/__init__.py b/api_hour/plugins/aiohttp/response.py
similarity index 68%
copy from api_hour/plugins/aiohttp/__init__.py
copy to api_hour/plugins/aiohttp/response.py
index 4c82405..7d31c93 100644
--- a/api_hour/plugins/aiohttp/__init__.py
+++ b/api_hour/plugins/aiohttp/response.py
@@ -1,4 +1,4 @@
-from aiohttp.web import Response
+from aiohttp.web import HTTPException
 
 try:
     import ujson as json
@@ -6,24 +6,25 @@ except ImportError:
     import json
 
 
-class JSON(Response):
+class JSON(HTTPException):
     """Serialize response to JSON with aiohttp.web"""
 
     def __init__(self, data, status=200,
                  reason=None, headers=None):
         body = json.dumps(data).encode('utf-8')
+        self.status_code = status
 
-        super().__init__(body=body, status=status, reason=reason,
+        super().__init__(body=body, reason=reason,
                          headers=headers, content_type='application/json')
 
 
-class HTML(Response):
+class HTML(HTTPException):
     """Serialize response to HTML with aiohttp.web"""
 
     def __init__(self, data, status=200,
                  reason=None, headers=None):
-
         body = data.encode('utf-8')
+        self.status_code = status
 
-        super().__init__(body=body, status=status, reason=reason,
-                         headers=headers, content_type='text/html')
\ No newline at end of file
+        super().__init__(body=body, reason=reason,
+                         headers=headers, content_type='text/html')
diff --git a/api_hour/plugins/aiohttp/router.py b/api_hour/plugins/aiohttp/router.py
new file mode 100644
index 0000000..0ad7731
--- /dev/null
+++ b/api_hour/plugins/aiohttp/router.py
@@ -0,0 +1,16 @@
+from aiohttp.web_urldispatcher import UrlDispatcher
+
+
+class Router(UrlDispatcher):
+    def __init__(self, container):
+        self._container = container
+        self._container.env_config = dict()
+        super().__init__()
+
+    def add_route(self, method, path, handler,
+                  *, name=None, expect_handler=None, env_config=None):
+        resource = self.add_resource(path, name=name)
+        if name:
+            self._container.env_config[name] = env_config or []
+        return resource.add_route(method, handler,
+                                  expect_handler=expect_handler)
diff --git a/api_hour/worker.py b/api_hour/worker.py
index 01f7c93..95e2bf1 100644
--- a/api_hour/worker.py
+++ b/api_hour/worker.py
@@ -7,6 +7,11 @@ import signal
 import sys
 import gunicorn.workers.base as base
 
+try:
+    import aiohttp.web
+except ImportError:
+    aiohttp = None
+
 # from pycallgraph import PyCallGraph
 # from pycallgraph import Config
 # from pycallgraph.output import GraphvizOutput
@@ -59,35 +64,38 @@ class Worker(base.Worker):
             self.handlers = None
 
             # stop accepting connections
+            self.log.info("Closing %s servers. PID: %s", len(servers), self.pid)
+            closing = list()
             for server, handler in servers.items():
-                if hasattr(handler, 'connections'):
-                    self.log.info("Stopping server: %s, connections: %s",
-                                  self.pid, len(handler.connections))
-                else:
-                    self.log.info("Stopping server: %s",
-                                  self.pid)
                 server.close()
+                closing.append(server.wait_closed())
+
+            if closing:
+                await asyncio.wait(closing, return_when=asyncio.ALL_COMPLETED, loop=self.loop)
 
-            # stop alive connections
+            self.log.debug('Shutting down')
+            await self.container.shutdown()
             tasks = []
             for handler in servers.values():
-                if hasattr(handler, 'finish_connections'):
-                    tasks.append(handler.finish_connections(
-                    timeout=self.cfg.graceful_timeout / 100 * 80))
+                if aiohttp and isinstance(handler, aiohttp.web.Server):
+                    tasks.append(handler.shutdown(timeout=self.cfg.graceful_timeout / 100 * 80))
             if tasks:
-                await asyncio.wait(tasks, loop=self.loop)
+                await asyncio.wait(tasks, loop=self.loop, return_when=asyncio.ALL_COMPLETED)
 
-            # stop container
-            await self.container.stop()
+            self.log.debug('Cleaning container')
+            await self.container.cleanup()
 
-            # Wait the end of close
-            for server, handler in servers.items():
-                await server.wait_closed()
+            self.log.debug('All server closed')
+
+        else:
+            await self.container.shutdown()
+            await self.container.cleanup()
 
     async def _run(self):
         self.container = self.app.callable(config=self.app.config,
                                            worker=self,
                                            loop=self.loop)
+        await self.container.pre_start()
         if asyncio.iscoroutinefunction(self.container.make_servers):
             self.handlers = await self.container.make_servers(self.sockets)
         else:
diff --git a/setup.cfg b/setup.cfg
index acb701c..ae4385d 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -5,7 +5,7 @@ description-file = README.rst
 universal = 1
 
 [egg_info]
-tag_date = 0
 tag_build = 
+tag_date = 0
 tag_svn_revision = 0
 
diff --git a/setup.py b/setup.py
index 584369b..bfab681 100644
--- a/setup.py
+++ b/setup.py
@@ -5,10 +5,12 @@ from setuptools import setup, find_packages
 
 __docformat__ = 'rst'
 
-PY_VER = sys.version_info
+PY_VER = sys.version_info[:3]
 
 if PY_VER < (3, 5, 0):
-    raise RuntimeError("api_hour doesn't support Python earlier than 3.5.0, current Python version is: %s" % PY_VER)
+    PY_VERS = '.'.join(map(str, PY_VER))
+    raise RuntimeError("api_hour doesn't support Python earlier than 3.5.0, "
+                       "current Python version is: %s" % PY_VERS)
 
 install_requires = ['gunicorn', 'PyYAML', 'setproctitle']
 

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/api-hour.git



More information about the Python-modules-commits mailing list