123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303 |
- #!/usr/bin/env python
- import sys
- import spyne.const
- spyne.const.MIN_GC_INTERVAL = float('inf')
- from lxml import html
- from random import randint, shuffle, choice
- from contextlib import closing
- from email.utils import formatdate
- from neurons import TableModel, Application
- from neurons.daemon import ServiceDefinition, HttpServer, StaticFileServer
- from neurons.daemon.main import Bootstrapper
- from spyne import Integer32, Unicode, rpc, ServiceBase, Integer, Array, Any
- from spyne.protocol.html import HtmlCloth
- from spyne.protocol.http import HttpRpc
- from spyne.protocol.json import JsonDocument
- from spyne.server.wsgi import WsgiApplication
- if sys.version_info[0] == 3:
- xrange = range
- _is_pypy = hasattr(sys, 'pypy_version_info')
- DBDRIVER = 'postgresql+psycopg2cffi' if _is_pypy else 'postgresql+psycopg2'
- DBHOST = 'tfb-database'
- # models
- class DbSessionManager(object):
- def __init__(self, config):
- self.session = config.get_main_store().Session()
- def close(self, with_err):
- self.session.close()
- class DbConnectionManager(object):
- def __init__(self, config):
- self.conn = config.get_main_store().engine.connect()
- def close(self, with_err):
- self.conn.close()
- class World(TableModel):
- __tablename__ = "world"
- _type_info = [
- ('id', Integer32(primary_key=True)),
- ('randomNumber', Integer32(sqla_column_args=dict(name='randomnumber'))),
- ]
- T_INDEX = html.fromstring(open('cloths/index.html', 'rb').read())
- class Fortune(TableModel):
- __tablename__ = "fortune"
- id = Integer32(primary_key=True)
- message = Unicode
- outprot_plain = HttpRpc(mime_type='text/plain')
- class TfbSimpleService(ServiceBase):
- @rpc(_returns=Any)
- def json(ctx):
- ctx.transport.add_header('Date', formatdate(usegmt=True))
- return dict(message=u'Hello, World!')
- @rpc(_returns=Any)
- def plaintext(ctx):
- """Test 6: Plaintext"""
- ctx.out_protocol = outprot_plain
- return b'Hello, World!'
- def _force_int(v):
- try:
- return min(500, max(int(v), 1))
- except:
- return 1
- NumQueriesType = Any(sanitizer=_force_int)
- class TfbOrmService(ServiceBase):
- @rpc(_returns=World)
- def db(ctx):
- retval = ctx.udc.session.query(World).get(randint(1, 10000))
- return retval
- @rpc(NumQueriesType, _returns=Array(World))
- def dbs(ctx, queries):
- if queries is None:
- queries = 1
- q = ctx.udc.session.query(World)
- return [q.get(randint(1, 10000)) for _ in xrange(queries)]
- @rpc(_returns=Array(Fortune, html_cloth=T_INDEX), _body_style='out_bare')
- def fortunes(ctx):
- # This is normally specified at the application level as it's a good
- # practice to group rpc endpoints with similar return types under the
- # same url fragment. eg. https://example.com/api/json
- ctx.out_protocol = HtmlCloth()
- ctx.outprot_ctx = ctx.out_protocol.get_context(ctx, ctx.transport)
- fortunes = ctx.udc.session.query(Fortune).all()
- fortunes.append(
- Fortune(id=0, message=u"Additional fortune added at request time.")
- )
- fortunes.sort(key=lambda x: x.message)
- return fortunes
- @rpc(NumQueriesType, _returns=Array(World))
- def updates(ctx, queries):
- """Test 5: Database Updates"""
- if queries is None:
- queries = 1
- retval = []
- q = ctx.udc.session.query(World)
- for id in (randint(1, 10000) for _ in xrange(queries)):
- world = q.get(id)
- world.randomNumber = randint(1, 10000)
- retval.append(world)
- ctx.udc.session.commit()
- return retval
- class TfbRawService(ServiceBase):
- @rpc(_returns=World)
- def dbraw(ctx):
- conn = ctx.udc.conn
- wid = randint(1, 10000)
- return conn.execute(
- "SELECT id, randomNumber FROM world WHERE id = %s", wid) \
- .fetchone()
- # returning both Any+dict or ObjectMarker+ListOfLists works
- @rpc(NumQueriesType, _returns=Any)
- def dbsraw(ctx, queries):
- if queries is None:
- queries = 1
- retval = []
- conn = ctx.udc.conn
- for i in xrange(queries):
- wid = randint(1, 10000)
- result = conn.execute(
- "SELECT id, randomNumber FROM world WHERE id = %s", wid) \
- .fetchone()
- retval.append(dict(id=result[0], randomNumber=result[1]))
- return retval
- @rpc(_returns=Array(Fortune, html_cloth=T_INDEX), _body_style='out_bare')
- def fortunesraw(ctx):
- # This is normally specified at the application level as it's a good
- # practice to group rpc endpoints with similar return types under the
- # same url fragment. eg. https://example.com/api/json
- ctx.out_protocol = HtmlCloth()
- ctx.outprot_ctx = ctx.out_protocol.get_context(ctx, ctx.transport)
- res = ctx.udc.conn.execute("SELECT id, message FROM fortune")
- fortunes = res.fetchall()
- fortunes.append(Fortune(
- id=0,
- message=u"Additional fortune added at request time."
- ))
- fortunes.sort(key=lambda x: x.message)
- return fortunes
- @rpc(NumQueriesType, _returns=Any)
- def updatesraw(ctx, queries):
- """Test 5: Database Updates"""
- if queries is None:
- queries = 1
- conn = ctx.udc.conn
- ids = [randint(1, 10000) for _ in xrange(queries)]
- retval = []
- for i in ids:
- wid, rn = conn.execute(
- "SELECT id, randomNumber FROM world WHERE id=%s", i) \
- .fetchone()
- rn = randint(1, 10000)
- retval.append(dict(id=wid, randomNumber=rn))
- conn.execute("UPDATE World SET randomNumber=%s WHERE id=%s",
- rn, wid)
- return retval
- def _on_method_call_db_sess(ctx):
- ctx.transport.add_header('Date', formatdate(usegmt=True))
- ctx.udc = DbSessionManager(ctx.app.config)
- def _on_method_call_db_conn(ctx):
- ctx.transport.add_header('Date', formatdate(usegmt=True))
- ctx.udc = DbConnectionManager(ctx.app.config)
- TfbRawService.event_manager.add_listener("method_call", _on_method_call_db_conn)
- TfbOrmService.event_manager.add_listener("method_call", _on_method_call_db_sess)
- def init_app(config):
- subconfig = config.services['root']
- app = Application(
- [TfbOrmService, TfbRawService, TfbSimpleService],
- tns='http://techempower.com/benchmarks/Python/Spyne',
- in_protocol=HttpRpc(),
- out_protocol=JsonDocument(),
- config=config,
- )
- if subconfig.subapps is None:
- subconfig.subapps = {}
- subconfig.subapps.update({'': app})
- return subconfig.gen_site()
- def init(config):
- return {
- 'root': ServiceDefinition(
- init=init_app,
- default=HttpServer(
- type='tcp4',
- host='127.0.0.1',
- port=8080,
- ),
- ),
- }
- def parse_config(argv):
- from neurons.daemon.main import boot
- retcode, config = boot('tfb', argv, init, bootstrapper=TfbBootstrap)
- if retcode is not None:
- sys.exit(retcode)
- return config
- def gen_wsgi_app():
- config = parse_config([])
- app = config.services['root'].subapps[''].app
- return WsgiApplication(app)
- words = 'some random words for you and me somebody else if then the'.split()
- class TfbBootstrap(Bootstrapper):
- # noinspection PyUnresolvedReferences
- def after_tables(self, config):
- print("Generating data...")
- with closing(config.get_main_store().Session()) as session:
- ints = list(range(10000))
- shuffle(ints)
- for _ in range(10000):
- session.add(World(randomNumber=ints.pop()))
- for _ in range(100):
- session.add(Fortune(
- message=' '.join([choice(words)
- for _ in range(randint(3, 10))])
- ))
- session.commit()
- if __name__ == '__main__':
- parse_config(sys.argv)
- else:
- application = gen_wsgi_app()
|