Explorar el Código

Update web2py tests to production quality (#2263)

Switch to a production web server, use database connection pooling,
turn off database migrations, remove unnecessary code, compile the
application, and make other minor tweaks.
abastardi hace 9 años
padre
commit
1ba2e5373a
Se han modificado 28 ficheros con 288 adiciones y 347 borrados
  1. 32 29
      frameworks/Python/web2py/README.md
  2. 0 2
      frameworks/Python/web2py/app/app/ABOUT
  3. 0 4
      frameworks/Python/web2py/app/app/LICENSE
  4. 0 76
      frameworks/Python/web2py/app/app/controllers/default.py
  5. 0 41
      frameworks/Python/web2py/app/app/models/db.py
  6. 0 19
      frameworks/Python/web2py/app/app/private/appconfig.ini
  7. 0 38
      frameworks/Python/web2py/app/app/routes.example.py
  8. 0 52
      frameworks/Python/web2py/app/app/views/default/index.html
  9. 0 35
      frameworks/Python/web2py/app/app/views/default/user.html
  10. 0 16
      frameworks/Python/web2py/app/app/views/generic.html
  11. 0 0
      frameworks/Python/web2py/app/optimized/__init__.py
  12. 9 0
      frameworks/Python/web2py/app/optimized/models/app.py
  13. 0 0
      frameworks/Python/web2py/app/optimized/views/__init__.py
  14. 3 5
      frameworks/Python/web2py/app/optimized/views/fortune.html
  15. 6 13
      frameworks/Python/web2py/app/routes.py
  16. 1 0
      frameworks/Python/web2py/app/standard/__init__.py
  17. 15 0
      frameworks/Python/web2py/app/standard/controllers/default.py
  18. 1 0
      frameworks/Python/web2py/app/standard/modules/__init__.py
  19. 53 0
      frameworks/Python/web2py/app/standard/modules/controller.py
  20. 56 0
      frameworks/Python/web2py/app/standard/modules/database.py
  21. 1 0
      frameworks/Python/web2py/app/standard/views/__init__.py
  22. 13 0
      frameworks/Python/web2py/app/standard/views/fortune.html
  23. 15 0
      frameworks/Python/web2py/app/wsgi.py
  24. 35 11
      frameworks/Python/web2py/benchmark_config.json
  25. 12 0
      frameworks/Python/web2py/compile_apps.py
  26. 22 0
      frameworks/Python/web2py/gunicorn_conf.py
  27. 4 1
      frameworks/Python/web2py/requirements.txt
  28. 10 5
      frameworks/Python/web2py/setup.sh

+ 32 - 29
frameworks/Python/web2py/README.md

@@ -1,45 +1,48 @@
-# web2py Benchmark Test 
+# [web2py](http://www.web2py.com/) Benchmarking Test
 
-Main controller found [here](web2py/applications/app/controllers/default.py)
+This is the web2py portion of a [benchmarking tests suite](../../)
+comparing a variety of web development platforms.
 
-Database model found [here](web2py/applications/app/models/db.py)
+The information below is specific to web2py. For further guidance,
+review the [documentation](http://frameworkbenchmarks.readthedocs.org/en/latest/).
+Also note that there is additional information provided in
+the [Python README](../).
 
-Fortunes template found [here](web2py/applications/app/views/default/fortune.html)
+There are two sets of web2py tests, "web2py-standard" (the default) and "web2py-optimized". The former set is implemented via standard web2py production code. The latter involves several special optimizations that would not be typical of a web2py application but could be implemented in cases were performance is critical.
 
-## Description
+## Web Server and Database Client Software
 
-web2py framework (http://www.web2py.com/)
+* [Meinheld](http://meinheld.org/) 0.5.9
+* [Gunicorn](http://gunicorn.org/) 19.4.5
+* [greenlet](http://greenlet.readthedocs.io/en/latest/) 0.4.9
+* [MySQLdb](https://mysqlclient.readthedocs.io/en/latest/) 1.3.7
 
-### Database
+## Test Paths
 
-MySQL
+"web2py-standard" and "web2py-optimized" tests are accessed via the "/standard" and "/optimized" paths, respectively.
 
-### Server
+* JSON Serialization: "/standard/json", "/optimized/json"
+* Single Database Query: "/standard/db", "/optimized/db"
+* Multiple Database Queries: "/standard/dbs?queries=#", "/optimized/dbs?queries=#"*
+* Fortunes: "/standard/fortunes", "/optimized/fortunes"
+* Database Updates: "/standard/update?queries=#", "/optimized/update?queries=#"*
+* Plaintext: "/standard/plaintext", "/optimized/plaintext"
 
-* Rocket (default web2py server)
+*Replace # with an actual number.
 
-## Test URLs
-### JSON Encoding
+## Source Code
 
-http://localhost:8080/json
+* [Database connection and models](app/standard/modules/database.py)
+* [Controller](app/standard/modules/controller.py)
+* [Fortunes template](app/standard/views/fortune.html)
 
-### Plaintext
+## Get Help
 
-http://localhost:8080/plaintext
+### [Community](http://web2py.com/init/default/documentation)
 
-### DB
+* [web2py-users Google Group](https://groups.google.com/forum/#!forum/web2py)
+* [web2py-developers Google Group](https://groups.google.com/forum/#!forum/web2py-developers)
 
-http://localhost:8080/db
-
-### Query
-
-http://localhost:8080/queries?queries=2
-
-### Update
-
-http://localhost:8080/updates?queries=2
-
-### Fortune
-
-http://localhost:8080/fortune
+### [Resources](http://web2py.com/init/default/documentation)
 
+* [*web2py: Complete Reference Manual*](http://web2py.com/book)

+ 0 - 2
frameworks/Python/web2py/app/app/ABOUT

@@ -1,2 +0,0 @@
-Write something about this app.
-Developed with web2py.

+ 0 - 4
frameworks/Python/web2py/app/app/LICENSE

@@ -1,4 +0,0 @@
-The web2py welcome app is licensed under public domain 
-(except for the css and js files that it includes, which have their own third party licenses).
-
-You can modify this license when you add your own code.

+ 0 - 76
frameworks/Python/web2py/app/app/controllers/default.py

@@ -1,76 +0,0 @@
-# -*- coding: utf-8 -*-
-from random import randint
-from functools import partial
-import json as jsonOut
-
-def getQueryNum(queryString):
-    try:
-        num_queries = int(queryString)
-        if num_queries < 1:
-            return 1
-        if num_queries > 500:
-            return 500
-        return num_queries
-    except ValueError:
-         return 1
-
-def serializeWorld(world):
-    return {
-        "id" : world.id,
-        "randomNumber" : world.randomNumber
-    }
-
-def serializeFortune(fortune):
-    return {
-        "id" : fortune.id,
-        "message": fortune.message
-    }
-
-def plaintext():
-    session.forget()
-    response.headers["Content-Type"]="text/plain; charset=UTF-8"
-    return "Hello, World!"
-
-def json():
-    session.forget()
-    response.headers["Content-Type"]="application/json; charset=UTF-8"
-    return jsonOut.dumps({"message":"Hello, World!"})
-
-def db():
-    session.forget()
-    response.headers["Content-Type"]="application/json; charset=UTF-8"
-    wid = randint(1, 10000)
-    world = DATABASE.world[wid]
-    return jsonOut.dumps(serializeWorld(world))
-
-def queries():
-    session.forget()
-    response.headers["Content-Type"]="application/json; charset=UTF-8"
-    num_queries = getQueryNum(request.vars["queries"])
-    rp = partial(randint, 1, 10000)
-    worlds = [serializeWorld(DATABASE.world[rp()]) for _ in xrange(num_queries)]
-    return jsonOut.dumps(worlds)
-
-def updates():
-    session.forget()
-    response.headers["Content-Type"]="application/json; charset=UTF-8"
-    num_queries = getQueryNum(request.vars["queries"])
-    worlds = []
-    rp = partial(randint, 1, 10000)
-    ids = [rp() for _ in xrange(num_queries)]
-    ids.sort() # To avoid deadlock
-    for id in ids:
-        world = DATABASE.world[id]
-        newNumber = rp()
-        DATABASE(DATABASE.world.id==id).update(randomNumber=newNumber)
-        world.randomNumber = newNumber
-        worlds.append(serializeWorld(world))
-    return jsonOut.dumps(worlds)
-
-def fortune():
-    session.forget()
-    fortunes = DATABASE(DATABASE.fortune).select()
-    fortune_list = fortunes.as_list();
-    fortune_list.append({"id":0, "message":"Additional fortune added at request time."})
-    fortune_list = sorted(fortune_list, key=lambda k: k["message"])
-    return dict(fortunes=fortune_list)

+ 0 - 41
frameworks/Python/web2py/app/app/models/db.py

@@ -1,41 +0,0 @@
-# -*- coding: utf-8 -*-
-
-#########################################################################
-## This scaffolding model makes your app work on Google App Engine too
-## File is released under public domain and you can use without limitations
-#########################################################################
-
-## if SSL/HTTPS is properly configured and you want all HTTP requests to
-## be redirected to HTTPS, uncomment the line below:
-# request.requires_https()
-
-## app configuration made easy. Look inside private/appconfig.ini
-from gluon.contrib.appconfig import AppConfig
-## once in production, remove reload=True to gain full speed
-myconf = AppConfig(reload=False)
-
-DATABASE = None
-DATABASE_URI = "mysql://benchmarkdbuser:[email protected]:3306/hello_world"
-
-db = DAL(DATABASE_URI, fake_migrate_all=False, migrate_enabled=False, pool_size=8, lazy_tables=True)
-DATABASE = db
-## store sessions and tickets there
-##session.connect(request, response, db=db)
-## or store session in Memcache, Redis, etc.
-## from gluon.contrib.memdb import MEMDB
-## from google.appengine.api.memcache import Client
-## session.connect(request, response, db = MEMDB(Client()))
-
-## by default give a view/generic.extension to all actions from localhost
-## none otherwise. a pattern can be 'controller/function.extension'
-response.generic_patterns = [] 
-
-db.define_table("world",
-    Field("id"),
-    Field("randomNumber")
-)
-
-db.define_table("fortune",
-    Field("id"),
-    Field("message")
-)

+ 0 - 19
frameworks/Python/web2py/app/app/private/appconfig.ini

@@ -1,19 +0,0 @@
-; App configuration
-
-; db configuration
-[db]
-uri       = sqlite://storage.sqlite
-migrate   = 1
-pool_size = 1
-
-; smtp address and credentials
-[smtp]
-server = smtp.gmail.com:587
-sender = [email protected]
-login  = username:password
-
-
-; form styling
-[forms]
-formstyle = bootstrap3_inline
-separator = 

+ 0 - 38
frameworks/Python/web2py/app/app/routes.example.py

@@ -1,38 +0,0 @@
-# -*- coding: utf-8 -*-
-
-#  This is an app-specific example router
-#
-#  This simple router is used for setting languages from app/languages directory
-#  as a part of the application path:  app/<lang>/controller/function
-#  Language from default.py or 'en' (if the file is not found) is used as
-#  a default_language
-#
-# See <web2py-root-dir>/router.example.py for parameter's detail
-#-------------------------------------------------------------------------------------
-# To enable this route file you must do the steps:
-#
-# 1. rename <web2py-root-dir>/router.example.py to routes.py
-# 2. rename this APP/routes.example.py to APP/routes.py
-#    (where APP - is your application directory)
-# 3. restart web2py (or reload routes in web2py admin interfase)
-#
-# YOU CAN COPY THIS FILE TO ANY APPLICATION'S ROOT DIRECTORY WITHOUT CHANGES!
-
-from fileutils import abspath
-from languages import read_possible_languages
-
-possible_languages = read_possible_languages(abspath('applications', app))
-#NOTE! app - is an application based router's parameter with name of an
-#            application. E.g.'welcome'
-
-routers = {
-    app: dict(
-        default_language = possible_languages['default'][0],
-        languages = [lang for lang in possible_languages
-                           if lang != 'default']
-    )
-}
-
-#NOTE! To change language in your application using these rules add this line
-#in one of your models files:
-#   if request.uri_language: T.force(request.uri_language)

+ 0 - 52
frameworks/Python/web2py/app/app/views/default/index.html

@@ -1,52 +0,0 @@
-{{left_sidebar_enabled,right_sidebar_enabled=False,('message' in globals())}}
-{{extend 'layout.html'}}
-
-{{block header}}
-    <header class="container-fluid background">
-      <div class="jumbotron text-center">
-        {{if response.title:}}
-        <h1>{{=response.title}}
-          <small>{{=response.subtitle or ''}}</small></h1>
-        {{pass}}
-      </div>
-    </header>
-{{end}}
-
-{{if 'message' in globals():}}
-<h2>{{=message}}</h2>
-<p class="lead">{{=T('How did you get here?')}}</p>
-<ol>
-  <li>{{=T('You are successfully running web2py')}}</li>
-  <li>{{=XML(T('You visited the url %s', A(request.env.path_info,_href=request.env.path_info)))}}</li>
-  <li>{{=XML(T('Which called the function %s located in the file %s',
-    (A(request.function+'()',_href='#'),
-    A('web2py/applications/%(application)s/controllers/%(controller)s.py' % request,
-    _href=URL('admin','default','peek', args=(request.application,'controllers',request.controller+'.py'))))))}}</li>
-  <li>{{=XML(T('The output of the file is a dictionary that was rendered by the view %s',
-    A('web2py/applications/%(application)s/views/%(controller)s/index.html' % request,
-    _href=URL('admin','default','peek',args=(request.application,'views',request.controller,'index.html')))))}}</li>
-  <li>{{=T('You can modify this application and adapt it to your needs')}}</li>
-</ol>
-{{elif 'content' in globals():}}
-{{=content}}
-{{else:}}
-{{=BEAUTIFY(response._vars)}}
-{{pass}}
-
-{{block right_sidebar}}
-<div class="panel panel-info">
-  <div class="panel-heading"><h3 class="panel-title"><a class="btn-block"
-      href="{{=URL('admin','default','index')}}">
-      <i class="glyphicon glyphicon-cog"></i>
-      {{=T("admin")}}
-    </a></h3></div>
-  <div class="panel-body">
-    {{=T("Don't know what to do?")}}
-  </div>
-  <ul class="list-group">
-    <li class="list-group-item">{{=A(T("Online examples"), _href=URL('examples','default','index'))}}</li>
-    <li class="list-group-item"><a href="http://web2py.com">web2py.com</a></li>
-    <li class="list-group-item"><a href="http://web2py.com/book">{{=T('Documentation')}}</a></li>
-  </ul>
-</div>
-{{end}}

+ 0 - 35
frameworks/Python/web2py/app/app/views/default/user.html

@@ -1,35 +0,0 @@
-{{extend 'layout.html'}}
-
-<h2>
-{{=T('Sign Up') if request.args(0) == 'register' else T('Log In') if request.args(0) == 'login' else T(request.args(0).replace('_',' ').title())}}
-</h2>
-
-<div class="container">
-    <div class="row">
-        <div id="web2py_user_form" class="col-lg-6">
-        {{
-        if request.args(0)=='login':
-            if not 'register' in auth.settings.actions_disabled:
-                form.add_button(T('Sign Up'),URL(args='register', vars={'_next': request.vars._next} if request.vars._next else None),_class='btn btn-default')
-            pass
-            if not 'request_reset_password' in auth.settings.actions_disabled:
-                form.add_button(T('Lost Password'),URL(args='request_reset_password'),_class='btn btn-default')
-            pass
-        pass
-        =form
-        }}
-        </div>
-    </div>
-</div>
-
-
-{{block page_js}}
-<script>
-    jQuery("#web2py_user_form input:visible:enabled:first").focus();
-{{if request.args(0)=='register':}}
-    web2py_validate_entropy(jQuery('#auth_user_password'),100);
-{{elif request.args(0)=='change_password':}}
-    web2py_validate_entropy(jQuery('#no_table_new_password'),100);
-{{pass}}
-</script>
-{{end page_js}}

+ 0 - 16
frameworks/Python/web2py/app/app/views/generic.html

@@ -1,16 +0,0 @@
-{{extend 'layout.html'}}
-{{"""
-
-You should not modify this file. 
-It is used as default when a view is not provided for your controllers
-
-"""}}
-<h2>{{=' '.join(x.capitalize() for x in request.function.split('_'))}}</h2>
-{{if len(response._vars)==1:}}
-{{=BEAUTIFY(response._vars.values()[0])}}
-{{elif len(response._vars)>1:}}
-{{=BEAUTIFY(response._vars)}}
-{{pass}}
-{{if request.is_local:}}
-{{=response.toolbar()}}
-{{pass}}

+ 0 - 0
frameworks/Python/web2py/app/app/__init__.py → frameworks/Python/web2py/app/optimized/__init__.py


+ 9 - 0
frameworks/Python/web2py/app/optimized/models/app.py

@@ -0,0 +1,9 @@
+# -*- coding: utf-8 -*-
+from gluon import current
+# Re-use the controller from the "standard" application.
+from applications.standard.modules import controller
+
+current.optimized = True # Flag used in controller.py to trigger DAL optimizations.
+
+# To avoid the overhead of executing a controller, the response is returned here.
+raise HTTP(200, getattr(controller, request.function)(), **current.response.headers)

+ 0 - 0
frameworks/Python/web2py/app/app/views/__init__.py → frameworks/Python/web2py/app/optimized/views/__init__.py


+ 3 - 5
frameworks/Python/web2py/app/app/views/default/fortune.html → frameworks/Python/web2py/app/optimized/views/fortune.html

@@ -6,10 +6,8 @@
   <body>
     <table><tr><th>id</th><th>message</th></tr>
     {{for fortune in fortunes:}}
-      <tr><td>{{=fortune["id"]}}</td><td>{{=fortune["message"]}}</td></tr>
-      {{pass}}
-      </table>
+      <tr><td>{{=fortune['id']}}</td><td>{{=fortune['message']}}</td></tr>
+    {{pass}}
+    </table>
   </body>
 </html>
-
-

+ 6 - 13
frameworks/Python/web2py/app/routes.py

@@ -1,14 +1,7 @@
-routers = dict(
-    BASE = dict(
-        default_application='app',
-    )
-)
+# -*- coding: utf-8 -*-
 
-routes_in = (
-	("/json", "/app/default/json"),
-	("/plaintext", "/app/default/plaintext"),
-        ("/db", "/app/default/db"),
-	("/queries", "/app/default/queries"),
-	("/updates", "/app/default/updates"),
-	("/fortune", "/app/default/fortune")
-)
+routes_in = [
+    ('/standard/$anything', '/standard/default/index/$anything'),
+    # Disable sessions on the /optimized route for additional speed-up.
+    ('/optimized/$anything', '/optimized/default/$anything', dict(web2py_disable_session=True))
+]

+ 1 - 0
frameworks/Python/web2py/app/standard/__init__.py

@@ -0,0 +1 @@
+

+ 15 - 0
frameworks/Python/web2py/app/standard/controllers/default.py

@@ -0,0 +1,15 @@
+# -*- coding: utf-8 -*-
+
+# Controller functions would typically be defined in this file but are instead
+# imported from a module to enable sharing with the "optimized" application.
+import controller
+from gluon import current
+
+current.optimized = False # Flag used in controller.py to trigger DAL optimizations.
+
+session.forget(response) # For speed-up when sessions are not needed.
+
+def index():
+    # The first URL arg specifies the controller function to be executed
+    # from the controller.py module.
+    return getattr(controller, request.args(0))()

+ 1 - 0
frameworks/Python/web2py/app/standard/modules/__init__.py

@@ -0,0 +1 @@
+

+ 53 - 0
frameworks/Python/web2py/app/standard/modules/controller.py

@@ -0,0 +1,53 @@
+# -*- coding: utf-8 -*-
+from random import randint
+from functools import partial
+import json as jsonOut
+
+from gluon import current
+from database import Dal, RawDal, num_queries
+
+def plaintext():
+    current.response.headers['Content-Type'] = 'text/plain'
+    return 'Hello, World!'
+
+def json():
+    current.response.headers['Content-Type'] = 'application/json'
+    return jsonOut.dumps({'message': 'Hello, World!'})
+
+def db():
+    current.response.headers['Content-Type']='application/json'
+    return jsonOut.dumps(Dal('World').get_world(randint(1, 10000)))
+
+def queries():
+    current.response.headers['Content-Type']='application/json'
+    db = RawDal() if current.optimized else Dal('World')
+    get_world = db.get_world
+    r10k = partial(randint, 1, 10000)
+    worlds = [get_world(r10k()) for _ in
+              xrange(num_queries(current.request.vars.queries))]
+    return jsonOut.dumps(worlds)
+
+def updates():
+    current.response.headers['Content-Type']='application/json'
+    db = RawDal() if current.optimized else Dal('World')
+    get_world = db.get_world
+    update_world = db.update_world
+    r10k = partial(randint, 1, 10000)
+    worlds = []
+    #ids = [r10k() for _ in xrange(num_queries)]
+    #ids.sort() # To avoid deadlock
+    for wid in (r10k() for _ in xrange(num_queries(current.request.vars.queries))):
+        world = get_world(wid)
+        newNumber = r10k()
+        world['randomNumber'] = newNumber
+        worlds.append(world)
+        update_world(wid, newNumber)
+    if current.optimized:
+        db.flush_world_updates() # Batch updates.
+    return jsonOut.dumps(worlds)
+
+def fortune():
+    new_message = {'id': 0, 'message': 'Additional fortune added at request time.'}
+    db = RawDal() if current.optimized else Dal('Fortune')
+    fortunes = db.get_fortunes(new_message=new_message)
+    return current.response.render('fortune.html', fortunes=fortunes)

+ 56 - 0
frameworks/Python/web2py/app/standard/modules/database.py

@@ -0,0 +1,56 @@
+# -*- coding: utf-8 -*-
+import os
+from operator import itemgetter
+from gluon.storage import Storage
+from gluon.dal import DAL, Field, Row
+
+DBHOST = os.environ.get('DBHOST', 'localhost')
+DATABASE_URI = 'mysql://benchmarkdbuser:benchmarkdbpass@%s:3306/hello_world' % DBHOST
+
+class Dal(object):
+    def __init__(self, table=None, pool_size=8):
+        self.db = DAL(DATABASE_URI, migrate_enabled=False, pool_size=pool_size)
+        if table == 'World':
+            self.db.define_table('World', Field('randomNumber', 'integer'))
+        elif table == 'Fortune':
+            self.db.define_table('Fortune', Field('message'))
+
+    def get_world(self, wid):
+        return self.db(self.db.World.id == wid).select(cacheable=True)[0].as_dict()
+
+    def update_world(self, wid, randomNumber):
+        self.db(self.db.World.id == wid).update(randomNumber=randomNumber)
+
+    def get_fortunes(self, new_message):
+        fortunes = self.db(self.db.Fortune).select(cacheable=True)
+        fortunes.records.append(Row(new_message))
+        return fortunes.sort(itemgetter('message'))
+
+class RawDal(Dal):
+    def __init__(self):
+        super(RawDal, self).__init__()
+        self.world_updates = []
+
+    def get_world(self, wid):
+        return self.db.executesql('SELECT * FROM World WHERE id = %s',
+                                  placeholders=[wid], as_dict=True)[0]
+
+    def update_world(self, wid, randomNumber):
+        self.world_updates.extend([randomNumber, wid])
+
+    def flush_world_updates(self):
+        query = ';'.join('UPDATE World SET randomNumber=%s WHERE id=%s'
+                         for _ in xrange(len(self.world_updates) / 2))
+        self.db.executesql(query, placeholders=self.world_updates)
+
+    def get_fortunes(self, new_message):
+        fortunes = self.db.executesql('SELECT * FROM Fortune', as_dict=True)
+        fortunes.append(new_message)
+        return sorted(fortunes, key=itemgetter('message'))
+
+def num_queries(queries):
+    try:
+        num = int(queries)
+        return 1 if num < 1 else 500 if num > 500 else num
+    except ValueError:
+         return 1

+ 1 - 0
frameworks/Python/web2py/app/standard/views/__init__.py

@@ -0,0 +1 @@
+

+ 13 - 0
frameworks/Python/web2py/app/standard/views/fortune.html

@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Fortunes</title>
+  </head>
+  <body>
+    <table><tr><th>id</th><th>message</th></tr>
+    {{for fortune in fortunes:}}
+      <tr><td>{{=fortune['id']}}</td><td>{{=fortune['message']}}</td></tr>
+    {{pass}}
+    </table>
+  </body>
+</html>

+ 15 - 0
frameworks/Python/web2py/app/wsgi.py

@@ -0,0 +1,15 @@
+import sys
+import os
+
+path = os.path.dirname(os.path.abspath(__file__))
+os.chdir(path)
+
+if not os.path.isdir('applications'):
+    raise RuntimeError('Running from the wrong folder')
+
+sys.path = [path] + [p for p in sys.path if not p == path]
+
+sys.stdout = sys.stderr
+
+from gluon.main import wsgibase
+application = wsgibase

+ 35 - 11
frameworks/Python/web2py/benchmark_config.json

@@ -3,25 +3,49 @@
   "tests": [{
     "default": {
       "setup_file": "setup",
-      "plaintext_url": "/plaintext",
-      "json_url": "/json",
-      "db_url": "/db",
-      "query_url": "/queries?queries=",
-      "fortune_url": "/fortune",
-      "update_url": "/updates?queries=",
+      "plaintext_url": "/standard/plaintext",
+      "json_url": "/standard/json",
+      "db_url": "/standard/db",
+      "query_url": "/standard/queries?queries=",
+      "fortune_url": "/standard/fortune",
+      "update_url": "/standard/updates?queries=",
       "port": 8080,
       "approach": "Realistic",
-      "classification": "Micro",
+      "classification": "Fullstack",
       "database": "MySQL",
       "framework": "web2py",
       "language": "Python",
       "orm": "Full",
-      "platform": "web2Py",
+      "platform": "Meinheld",
       "webserver": "None",
       "os": "Linux",
       "database_os": "Linux",
-      "display_name": "web2py",
-      "notes": "CPython 2.7"
-    } 
+      "display_name": "web2py-standard",
+      "notes": "CPython 2.7",
+      "versus": "wsgi"
+    },
+    "optimized": {
+      "setup_file": "setup",
+      "plaintext_url": "/optimized/plaintext",
+      "json_url": "/optimized/json",
+      "db_url": "/optimized/db",
+      "query_url": "/optimized/queries?queries=",
+      "fortune_url": "/optimized/fortune",
+      "update_url": "/optimized/updates?queries=",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "MySQL",
+      "framework": "web2py",
+      "language": "Python",
+      "orm": "Full",
+      "platform": "Meinheld",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "web2py-optimized",
+      "notes": "CPython 2.7",
+      "versus": "wsgi"
+    }
   }]
 }

+ 12 - 0
frameworks/Python/web2py/compile_apps.py

@@ -0,0 +1,12 @@
+# -*- coding: utf-8 -*-
+import sys
+import os
+
+path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'web2py')
+
+sys.path = [path] + [p for p in sys.path if not p == path]
+
+from gluon.compileapp import compile_application
+
+compile_application(os.path.join(path, 'applications', 'standard'))
+compile_application(os.path.join(path, 'applications', 'optimized'))

+ 22 - 0
frameworks/Python/web2py/gunicorn_conf.py

@@ -0,0 +1,22 @@
+import multiprocessing
+import os
+import sys
+
+_is_travis = os.environ.get('TRAVIS') == 'true'
+
+workers = multiprocessing.cpu_count() * 3
+if _is_travis:
+    workers = 2
+
+bind = "0.0.0.0:8080"
+keepalive = 120
+errorlog = '-'
+pidfile = 'gunicorn.pid'
+pythonpath = 'web2py'
+
+worker_class = "meinheld.gmeinheld.MeinheldWorker"
+
+def post_fork(server, worker):
+    # Disable access log
+    import meinheld.server
+    meinheld.server.set_access_logger(None)

+ 4 - 1
frameworks/Python/web2py/requirements.txt

@@ -1 +1,4 @@
-mysqlclient==1.3.6
+mysqlclient==1.3.7
+gunicorn==19.4.5
+meinheld==0.5.9
+greenlet==0.4.9

+ 10 - 5
frameworks/Python/web2py/setup.sh

@@ -2,13 +2,18 @@
 
 fw_depends python2
 
-sed -i 's|127.0.0.1|'${DBHOST}'|g' app/app/models/db.py
-
 pip install --install-option="--prefix=${PY2_ROOT}" -r $TROOT/requirements.txt
 
 rm -fr web2py
-git clone --recursive --branch R-2.10.3 https://github.com/web2py/web2py.git 
-cp -r app/app/ web2py/applications/
+git clone --recursive --branch master https://github.com/web2py/web2py.git
+cd web2py
+git checkout 326335c3cd027a97b9c2b83d880b2b1f8f62321d
+cd ..
+cp -r app/standard/ web2py/applications/
+cp -r app/optimized/ web2py/applications/
+cp app/wsgi.py web2py/
 cp app/routes.py web2py/
+touch web2py/__init__.py
+python compile_apps.py
 
-python web2py/web2py.py -a '' -i 0.0.0.0 -p 8080  &
+gunicorn web2py.wsgi:application -c gunicorn_conf.py &