diff --git a/falcon/bench/bench.py b/falcon/bench/bench.py index ce70225..599f3c3 100755 --- a/falcon/bench/bench.py +++ b/falcon/bench/bench.py @@ -254,11 +254,12 @@ def run(frameworks, trials, iterations, stat_memory): def main(): frameworks = [ 'bottle', + 'django', 'falcon', 'falcon-ext', 'flask', 'pecan', - 'werkzeug' + 'werkzeug', ] parser = argparse.ArgumentParser(description='Falcon benchmark runner') @@ -313,7 +314,7 @@ def main(): us_per_req = (sec_per_req * Decimal(10 ** 6)) factor = round_to_int(baseline / sec_per_req) - print('{3}. {0:.<15s}{1:.>06d} req/sec or {2: >3.2f} μs/req ({4}x)'. + print('{3}. {0:.<20s}{1:.>06d} req/sec or {2: >3.2f} μs/req ({4}x)'. format(name, req_per_sec, us_per_req, i + 1, factor)) if heapy and args.stat_memory: diff --git a/falcon/bench/create.py b/falcon/bench/create.py index e12327d..3b976f6 100644 --- a/falcon/bench/create.py +++ b/falcon/bench/create.py @@ -96,59 +96,26 @@ def werkzeug(body, headers): return hello -def cherrypy(body, headers): - import cherrypy - - # Disable logging - cherrypy.config.update({'environment': 'embedded'}) - - class HelloResource(object): - - exposed = True - - def GET(self, account_id, test, limit=8): - user_agent = cherrypy.request.headers['User-Agent'] # NOQA - for name, value in headers.items(): - cherrypy.response.headers[name] = value - - return body - - class Root(object): - pass - - root = Root() - root.hello = HelloResource() - - conf = { - '/': { - 'request.dispatch': cherrypy.dispatch.MethodDispatcher(), - } - } - - app = cherrypy.tree.mount(root, '/', conf) - return app - - -# def wsme(body, headers): -# import wsme - -# class HelloService(wsme.WSRoot): - -# @wsme.expose(str, str) -# def hello(self, limit='10'): -# import pdb -# pdb.set_trace() -# return body - -# ws = HelloService(protocols=['restjson']) -# return ws.wsgiapp() - - def pecan(body, headers): - import falcon.bench.nuts.nuts.app as nuts + import pecan + pecan.x_test_body = body + pecan.x_test_headers = headers + import falcon.bench.nuts.nuts.app as nuts sys.path.append(os.path.dirname(nuts.__file__)) app = nuts.create() del sys.path[-1] return app + + +def django(body, headers): + import django + django.x_test_body = body + django.x_test_headers = headers + + from falcon.bench import dj + sys.path.append(os.path.dirname(dj.__file__)) + + from falcon.bench.dj.dj import wsgi + return wsgi.application diff --git a/falcon/bench/dj/__init__.py b/falcon/bench/dj/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/falcon/bench/dj/dj/__init__.py b/falcon/bench/dj/dj/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/falcon/bench/dj/dj/settings.py b/falcon/bench/dj/dj/settings.py new file mode 100644 index 0000000..d5a2b00 --- /dev/null +++ b/falcon/bench/dj/dj/settings.py @@ -0,0 +1,121 @@ +""" +Django settings for dj project. + +Generated by 'django-admin startproject' using Django 1.11.3. + +For more information on this file, see +https://docs.djangoproject.com/en/1.11/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/1.11/ref/settings/ +""" + +import os + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 't6gj%z0ee)*xmdoqhm4^er60=s^1g_vs7y!dl2vve4-1u9^+ps' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = ['*'] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'hello', +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'dj.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'dj.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/1.11/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + } +} + + +# Password validation +# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/1.11/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/1.11/howto/static-files/ + +STATIC_URL = '/static/' diff --git a/falcon/bench/dj/dj/urls.py b/falcon/bench/dj/dj/urls.py new file mode 100644 index 0000000..b8e747e --- /dev/null +++ b/falcon/bench/dj/dj/urls.py @@ -0,0 +1,7 @@ +from django.conf.urls import url + +from hello import views + +urlpatterns = [ + url(r'^hello/(?P[0-9]+)/test$', views.hello) +] diff --git a/falcon/bench/dj/dj/wsgi.py b/falcon/bench/dj/dj/wsgi.py new file mode 100644 index 0000000..cd4a418 --- /dev/null +++ b/falcon/bench/dj/dj/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for dj project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'dj.settings') + +application = get_wsgi_application() diff --git a/falcon/bench/dj/hello/__init__.py b/falcon/bench/dj/hello/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/falcon/bench/dj/hello/admin.py b/falcon/bench/dj/hello/admin.py new file mode 100644 index 0000000..a3846a1 --- /dev/null +++ b/falcon/bench/dj/hello/admin.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +# from django.contrib import admin + +# Register your models here. diff --git a/falcon/bench/dj/hello/apps.py b/falcon/bench/dj/hello/apps.py new file mode 100644 index 0000000..3ffd997 --- /dev/null +++ b/falcon/bench/dj/hello/apps.py @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.apps import AppConfig + + +class HelloConfig(AppConfig): + name = 'hello' diff --git a/falcon/bench/dj/hello/migrations/__init__.py b/falcon/bench/dj/hello/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/falcon/bench/dj/hello/models.py b/falcon/bench/dj/hello/models.py new file mode 100644 index 0000000..1e8e4e1 --- /dev/null +++ b/falcon/bench/dj/hello/models.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +# from django.db import models + +# Create your models here. diff --git a/falcon/bench/dj/hello/tests.py b/falcon/bench/dj/hello/tests.py new file mode 100644 index 0000000..c2de5b3 --- /dev/null +++ b/falcon/bench/dj/hello/tests.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +# from django.test import TestCase + +# Create your tests here. diff --git a/falcon/bench/dj/hello/views.py b/falcon/bench/dj/hello/views.py new file mode 100644 index 0000000..9a98a77 --- /dev/null +++ b/falcon/bench/dj/hello/views.py @@ -0,0 +1,17 @@ +import django +from django.http import HttpResponse + + +_body = django.x_test_body +_headers = django.x_test_headers + + +def hello(request, account_id): + user_agent = request.META['HTTP_USER_AGENT'] # NOQA + limit = request.GET.get('limit', '10') # NOQA + response = HttpResponse(_body) + + for name, value in _headers.items(): + response[name] = value + + return response diff --git a/falcon/bench/dj/manage.py b/falcon/bench/dj/manage.py new file mode 100755 index 0000000..1ed3638 --- /dev/null +++ b/falcon/bench/dj/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +import os +import sys + +if __name__ == '__main__': + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'dj.settings') + try: + from django.core.management import execute_from_command_line + except ImportError: + # The above import may fail for some other reason. Ensure that the + # issue is really that Django is missing to avoid masking other + # exceptions on Python 2. + try: + import django # NOQA + except ImportError: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + 'available on your PYTHONPATH environment variable? Did you ' + 'forget to activate a virtual environment?' + ) + raise + execute_from_command_line(sys.argv) diff --git a/falcon/bench/nuts/nuts/controllers/root.py b/falcon/bench/nuts/nuts/controllers/root.py index 4272b69..5623a79 100644 --- a/falcon/bench/nuts/nuts/controllers/root.py +++ b/falcon/bench/nuts/nuts/controllers/root.py @@ -1,16 +1,11 @@ import random +import pecan from pecan import expose, response, request -def rand_string(min, max): - int_gen = random.randint - string_length = int_gen(min, max) - return ''.join([chr(int_gen(ord('\t'), ord('~'))) - for i in range(string_length)]) - - -body = rand_string(10240, 10240) +_body = pecan.x_test_body +_headers = pecan.x_test_headers class TestController(object): @@ -20,10 +15,10 @@ class TestController(object): @expose(content_type='text/plain') def test(self): user_agent = request.headers['User-Agent'] # NOQA - limit = request.params['limit'] # NOQA - response.headers['X-Test'] = 'Funky Chicken' + limit = request.params.get('limit', '10') # NOQA + response.headers.update(_headers) - return body + return _body class HelloController(object): @@ -36,7 +31,7 @@ class RootController(object): @expose(content_type='text/plain') def index(self): - response.headers['X-Test'] = 'Funky Chicken' - return body + response.headers.update(_headers) + return _body hello = HelloController() diff --git a/falcon/bench/requirements.txt b/falcon/bench/requirements.txt new file mode 100644 index 0000000..c995544 --- /dev/null +++ b/falcon/bench/requirements.txt @@ -0,0 +1,5 @@ +bottle +django +flask +guppy +pecan