├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── gtornado ├── __init__.py ├── green.py ├── handler.py ├── memcache.py ├── mysql.py ├── redis.py ├── requests.py └── utils.py ├── setup.py └── tests ├── README.md ├── orm_storm.py ├── requirements.txt ├── test.py ├── test.sql ├── test_greenify.py ├── test_handler.py ├── test_memcache.py ├── test_mysqldb.py ├── test_pure_mysql.py ├── test_redis.py ├── test_requests.py └── test_travis.py /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | *.pyc 3 | dist/ 4 | *.egg-info/ 5 | *.swp 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | 3 | python: 4 | - 2.7 5 | 6 | install: 7 | - pip install tornado 8 | - pip install greenlet 9 | - pip install pymemcache 10 | - pip install PyMySQL 11 | - pip install storm 12 | 13 | 14 | before_script: 15 | - python setup.py install 16 | 17 | script: 18 | - cd tests 19 | - python test_travis.py& 20 | - pid=$! 21 | - kill $pid 22 | 23 | branches: 24 | except: 25 | - legacy 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Alexzhang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gTornado 2 | **gTornado** add greenify support to tornado. inspired by **motor and gevent** 3 | 4 | # Thanks 5 | **greenify** https://github.com/douban/greenify 6 | 7 | # Build Status 8 | [![Build Status](https://travis-ci.org/alex8224/gTornado.svg?branch=0.1)](https://travis-ci.org/alex8224/gTornado) 9 | 10 | #What **gTornado** can do? 11 | 1. make orm async seamless 12 | 2. make blockio async in tornado 13 | 3. adapte greenify for gTornado https://github.com/alex8224/greenify 14 | 15 | #Tested C library 16 | 1. libmysqlclient.so 17 | 2. libmemcached.so 18 | 19 | # Requirement 20 | 1. greenlet 21 | 2. pymysql 22 | 3. pymemcache 23 | 24 | # Test requirement 25 | 1. pymysql 26 | 2. storm 27 | 4. pymemcache 28 | -------------------------------------------------------------------------------- /gtornado/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | from .green import AsyncSocket, spawn, IS_PYPY 3 | from .handler import Handler 4 | -------------------------------------------------------------------------------- /gtornado/green.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | from __future__ import absolute_import 3 | import sys 4 | import socket 5 | import time 6 | import greenlet 7 | from functools import wraps 8 | 9 | from tornado.ioloop import IOLoop 10 | from tornado.iostream import IOStream 11 | from tornado.concurrent import Future 12 | from tornado.gen import coroutine, Return 13 | from tornado.netutil import Resolver 14 | 15 | 16 | IS_PYPY = False 17 | try: 18 | import __pypy__ 19 | __pypy__ 20 | IS_PYPY = True 21 | except: 22 | pass 23 | 24 | def set_resolver(resolver): 25 | Resolver.configure(resolver) 26 | 27 | def enable_debug(): 28 | if IS_PYPY: 29 | sys.stderr.write("settrace api unsupported on pypy") 30 | sys.stderr.flush() 31 | return 32 | 33 | import inspect 34 | def trace_green(event, args): 35 | src, target = args 36 | if event == "switch": 37 | print("from %s switch to %s" % (src, target)) 38 | elif event == "throw": 39 | print("from %s throw exception to %s" % (src, target)) 40 | 41 | if src.gr_frame: 42 | tracebacks = inspect.getouterframes(src.gr_frame) 43 | buff = [] 44 | for traceback in tracebacks: 45 | srcfile, lineno, func_name, codesample = traceback[1:-1] 46 | trace_line = '''File "%s", line %d, in %s\n%s ''' 47 | buff.append(trace_line % (srcfile, lineno, func_name, "".join(codesample))) 48 | 49 | print("".join(buff)) 50 | 51 | greenlet.settrace(trace_green) 52 | 53 | 54 | __all__ = ("IS_PYPY", "spawn", "AsyncSocket", "GreenTask", "synclize", 55 | "Waiter", "sleep", "Timeout", "Event", "Watcher", "Pool") 56 | 57 | 58 | class Hub(object): 59 | def __init__(self): 60 | self._greenlet = greenlet.getcurrent() 61 | self._ioloop = IOLoop.current() 62 | 63 | @property 64 | def greenlet(self): 65 | return self._greenlet 66 | 67 | def switch(self): 68 | self._greenlet.switch() 69 | 70 | @property 71 | def ioloop(self): 72 | return self._ioloop 73 | 74 | def run_later(self, deadline, callback, *args, **kwargs): 75 | return self.ioloop.add_timeout(time.time() + deadline, 76 | callback, *args, **kwargs) 77 | 78 | def run_callback(self, callback, *args, **kwargs): 79 | self.ioloop.add_callback(callback, *args, **kwargs) 80 | 81 | 82 | hub = Hub() 83 | 84 | def get_hub(): 85 | return hub 86 | 87 | 88 | class GreenTask(greenlet.greenlet): 89 | def __init__(self, run, *args, **kwargs): 90 | super(GreenTask, self).__init__() 91 | self._run = run 92 | self._args = args 93 | self._kwargs = kwargs 94 | self._future = Future() 95 | self._result = None 96 | self._exc_info = () 97 | 98 | @property 99 | def args(self): 100 | return self._args 101 | 102 | @property 103 | def kwargs(self): 104 | return self._kwargs 105 | 106 | def run(self): 107 | try: 108 | timeout = self.kwargs.pop("timeout", 0) 109 | if timeout: 110 | timer = Timeout(timeout) 111 | timer.start() 112 | self._result = self._run(*self.args, **self.kwargs) 113 | self._future.set_result(self._result) 114 | except: 115 | self._exc_info = sys.exc_info() 116 | self._future.set_exc_info(self._exc_info) 117 | finally: 118 | if timeout: 119 | timer.cancel() 120 | 121 | def start(self): 122 | self.switch() 123 | 124 | def __str__(self): 125 | func_name = "%s of %s " % (self._run.__name__, self._run.__module__) 126 | return "" % (func_name, hex(id(self))) 127 | 128 | def __repr__(self): 129 | return self.__str__() 130 | 131 | def wait(self): 132 | return self._future 133 | 134 | @classmethod 135 | def spawn(cls_green, *args, **kwargs): 136 | task = cls_green(*args, **kwargs) 137 | task.start() 138 | return task 139 | 140 | 141 | def synclize(func): 142 | coro = coroutine(func) 143 | @wraps(func) 144 | def _sync_call(*args, **kwargs): 145 | child_gr = greenlet.getcurrent() 146 | main = child_gr.parent 147 | assert main, "only run in child greenlet" 148 | 149 | def callback(future): 150 | if future.exc_info(): 151 | child_gr.throw(*future.exc_info()) 152 | elif future.exception(): 153 | child_gr.throw(future.exception()) 154 | else: 155 | child_gr.switch(future.result()) 156 | 157 | IOLoop.current().add_future(coro(*args, **kwargs), callback) 158 | return main.switch() 159 | return _sync_call 160 | 161 | def spawn(callable_obj, *args, **kwargs): 162 | return GreenTask.spawn(callable_obj, *args, **kwargs).wait() 163 | 164 | class Waiter(object): 165 | def __init__(self): 166 | self._greenlet = greenlet.getcurrent() 167 | self._main = self._greenlet.parent 168 | 169 | @property 170 | def greenlet(self): 171 | return self._greenlet 172 | 173 | def switch(self, value): 174 | self._greenlet.switch(value) 175 | 176 | def throw(self, *exc_info): 177 | self._greenlet.throw(*exc_info) 178 | 179 | def get(self): 180 | return self._main.switch() 181 | 182 | def clear(self): 183 | pass 184 | 185 | 186 | def sleep(seconds): 187 | waiter = Waiter() 188 | unique = object() 189 | IOLoop.current().add_timeout(time.time() + seconds, waiter.switch, unique) 190 | waiter.get() 191 | 192 | 193 | class TimeoutException(Exception): pass 194 | 195 | 196 | class Timeout(object): 197 | def __init__(self, deadline, ex=TimeoutException): 198 | self._greenlet = greenlet.getcurrent() 199 | self._ex = ex 200 | self._callback = None 201 | self._deadline = deadline 202 | self._delta = time.time() + deadline 203 | self._ioloop = IOLoop.current() 204 | 205 | def start(self, callback=None): 206 | errmsg = "%s timeout, deadline is %d seconds" % ( 207 | str(self._greenlet), self._deadline) 208 | if callback: 209 | self._callback = self._ioloop.add_timeout(self._delta, 210 | callback, 211 | self._ex(errmsg)) 212 | else: 213 | self._callback = self._ioloop.add_timeout(self._delta, 214 | self._greenlet.throw, 215 | self._ex(errmsg)) 216 | 217 | def cancel(self): 218 | assert self._callback, "Timeout not started" 219 | self._ioloop.remove_timeout(self._callback) 220 | self._greenlet = None 221 | 222 | 223 | class Event(object): 224 | def __init__(self): 225 | self._waiter = [] 226 | self._ioloop = IOLoop.current() 227 | 228 | def set(self): 229 | self._ioloop.add_callback(self._notify) 230 | 231 | def wait(self, timeout=None): 232 | current_greenlet = greenlet.getcurrent() 233 | self._waiter.append(current_greenlet.switch) 234 | waiter = Waiter() 235 | if timeout: 236 | timeout_checker = Timeout(timeout) 237 | timeout_checker.start(current_greenlet.throw) 238 | waiter.get() 239 | timeout_checker.cancel() 240 | else: 241 | waiter.get() 242 | 243 | def _notify(self): 244 | for waiter in self._waiter: 245 | waiter(self) 246 | 247 | 248 | class Watcher(object): 249 | def __init__(self, fd, events): 250 | self._fd = fd 251 | self._watched_event = IOLoop.READ if events == 1 else IOLoop.WRITE 252 | self._value = None 253 | self._greenlet = greenlet.getcurrent() 254 | self._main = self._greenlet.parent 255 | self._ioloop = IOLoop.current() 256 | self._callback = None 257 | self._iohandler = None 258 | 259 | def start(self, callback, args): 260 | self._callback = callback 261 | self._value = args 262 | self._ioloop.add_handler(self._fd, self._handle_event, self._watched_event) 263 | 264 | def _handle_event(self, fd, events): 265 | self._callback(self._value) 266 | 267 | def stop(self): 268 | self._ioloop.remove_handler(self._fd) 269 | 270 | 271 | class AsyncSocket(object): 272 | def __init__(self, sock): 273 | self._iostream = IOStream(sock) 274 | self._resolver = Resolver() 275 | self._readtimeout = 0 276 | self._connecttimeout = 0 277 | 278 | def set_readtimeout(self, timeout): 279 | self._readtimeout = timeout 280 | 281 | def set_connecttimeout(self, timeout): 282 | self._connecttimeout = timeout 283 | 284 | @synclize 285 | def connect(self, address): 286 | host, port = address 287 | timer = None 288 | try: 289 | if self._connecttimeout: 290 | timer = Timeout(self._connecttimeout) 291 | timer.start() 292 | resolved_addrs = yield self._resolver.resolve(host, port, family=socket.AF_INET) 293 | for addr in resolved_addrs: 294 | family, host_port = addr 295 | yield self._iostream.connect(host_port) 296 | break 297 | except TimeoutException: 298 | self.close() 299 | raise 300 | finally: 301 | if timer: 302 | timer.cancel() 303 | #@synclize 304 | def sendall(self, buff): 305 | self._iostream.write(buff) 306 | 307 | @synclize 308 | def read(self, nbytes, partial=False): 309 | timer = None 310 | try: 311 | if self._readtimeout: 312 | timer = Timeout(self._readtimeout) 313 | timer.start() 314 | buff = yield self._iostream.read_bytes(nbytes, partial=partial) 315 | raise Return(buff) 316 | except TimeoutException: 317 | self.close() 318 | raise 319 | finally: 320 | if timer: 321 | timer.cancel() 322 | 323 | def recv(self, nbytes): 324 | return self.read(nbytes, partial=True) 325 | 326 | @synclize 327 | def readline(self, max_bytes=-1): 328 | timer = None 329 | if self._readtimeout: 330 | timer = Timeout(self._readtimeout) 331 | timer.start() 332 | try: 333 | if max_bytes > 0: 334 | buff = yield self._iostream.read_until('\n', max_bytes=max_bytes) 335 | else: 336 | buff = yield self._iostream.read_until('\n') 337 | raise Return(buff) 338 | except TimeoutException: 339 | self.close() 340 | raise 341 | finally: 342 | if timer: 343 | timer.cancel() 344 | 345 | def close(self): 346 | self._iostream.close() 347 | 348 | def set_nodelay(self, flag): 349 | self._iostream.set_nodelay(flag) 350 | 351 | def settimeout(self, timeout): 352 | pass 353 | 354 | def shutdown(self, direction): 355 | if self._iostream.fileno(): 356 | self._iostream.fileno().shutdown(direction) 357 | 358 | def recv_into(self, buff): 359 | expected_rbytes = len(buff) 360 | data = self.read(expected_rbytes, True) 361 | srcarray = bytearray(data) 362 | nbytes = len(srcarray) 363 | buff[0:nbytes] = srcarray 364 | return nbytes 365 | 366 | def makefile(self, mode, other): 367 | return self 368 | 369 | 370 | class Pool(object): 371 | def __init__(self, max_size=-1, params={}): 372 | self._maxsize = max_size 373 | self._conn_params = params 374 | self._pool = [] 375 | self._started = False 376 | self._ioloop = IOLoop.current() 377 | self._event = Event() 378 | self._ioloop.add_future( 379 | spawn(self.start), 380 | lambda future: future) 381 | 382 | def create_raw_conn(self): 383 | pass 384 | 385 | def init_pool(self): 386 | for index in range(self._maxsize): 387 | conn = self.create_raw_conn() 388 | self._pool.append(conn) 389 | 390 | @property 391 | def size(self): 392 | return len(self._pool) 393 | 394 | def get_conn(self): 395 | if self.size > 0: 396 | return self._pool.pop(0) 397 | else: 398 | raise Exception("no available connections", self.size) 399 | 400 | def release(self, conn): 401 | self._pool.append(conn) 402 | 403 | def quit(self): 404 | self._started = False 405 | self._event.set() 406 | 407 | def _close_all(self): 408 | for conn in self._pool: 409 | conn.close() 410 | self._pool = None 411 | 412 | def start(self): 413 | self.init_pool() 414 | self._started = True 415 | self._event.wait() 416 | self._close_all() 417 | -------------------------------------------------------------------------------- /gtornado/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | from tornado.web import RequestHandler 3 | from gtornado import green 4 | # from greenlet import greenlet 5 | 6 | class Handler(RequestHandler): 7 | def _execute(self, *args, **kwargs): 8 | orgi_execute = super(Handler, self)._execute 9 | return green.spawn(orgi_execute, *args, **kwargs) 10 | -------------------------------------------------------------------------------- /gtornado/memcache.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | from __future__ import absolute_import 3 | from gtornado import AsyncSocket, green 4 | import socket 5 | 6 | __all__ = ("MemCachePool", ) 7 | 8 | 9 | class AsyncSocketModule(AsyncSocket): 10 | AF_INET = socket.AF_INET 11 | SOCK_STREAM = socket.SOCK_STREAM 12 | def __init__(self, sock_inet, sock_type): 13 | self._sock = socket.socket(sock_inet, sock_type) 14 | super(AsyncSocketModule, self).__init__(self._sock) 15 | 16 | @staticmethod 17 | def socket(sock_inet, sock_type): 18 | return AsyncSocketModule(sock_inet, sock_type) 19 | 20 | def settimeout(self, timeout): 21 | pass 22 | 23 | def setsockopt(self, proto, option, value): 24 | self.set_nodelay(value) 25 | 26 | def recv(self, nbytes): 27 | return self.read(nbytes, partial=True) 28 | 29 | 30 | from pymemcache.client.base import Client 31 | 32 | 33 | class MemCachePool(green.Pool): 34 | def create_raw_conn(self): 35 | servers = self._conn_params["servers"] 36 | return Client(servers, socket_module=AsyncSocketModule) 37 | 38 | def _close_all(self): 39 | for conn in self._pool: 40 | conn.quit() 41 | self._pool = None 42 | -------------------------------------------------------------------------------- /gtornado/mysql.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | from __future__ import absolute_import 3 | import sys 4 | import socket 5 | from pymysql.connections import Connection 6 | from gtornado import AsyncSocket, green, utils 7 | 8 | __all__ = ("AsyncConnection", "MySQLConnectionPool", "patch_pymysql") 9 | 10 | class AsyncConnection(Connection): 11 | def __init__(self, *args, **kwargs): 12 | super(AsyncConnection, self).__init__(*args, **kwargs) 13 | 14 | def connect(self, sock=None): 15 | try: 16 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 17 | self.socket = AsyncSocket(sock) 18 | self.socket.connect((self.host, self.port)) 19 | self.socket.set_nodelay(True) 20 | self._rfile = self.socket 21 | self._get_server_information() 22 | self._request_authentication() 23 | 24 | if self.sql_mode is not None: 25 | c = self.cursor() 26 | c.execute("SET sql_mode=%s", (self.sql_mode,)) 27 | 28 | if self.init_command is not None: 29 | c = self.cursor() 30 | c.execute(self.init_command) 31 | c.close() 32 | self.commit() 33 | 34 | if self.autocommit_mode is not None: 35 | self.autocommit(self.autocommit_mode) 36 | except socket.error: 37 | if self.socket: 38 | self.socket.close() 39 | raise 40 | 41 | 42 | class MySQLConnectionPool(green.Pool): 43 | __metaclass__ = utils.Singleton 44 | 45 | def __init__(self, max_size=-1, mysql_params={}): 46 | super(MySQLConnectionPool, self).__init__(max_size, mysql_params) 47 | 48 | def create_raw_conn(self): 49 | return AsyncConnection( 50 | host=self._conn_params["host"], 51 | port=self._conn_params["port"], 52 | user=self._conn_params["username"], 53 | db=self._conn_params["db"], 54 | password=self._conn_params["password"], 55 | charset=self._conn_params.get("charset", "utf8") 56 | ) 57 | 58 | 59 | class ConnectionProxy(object): 60 | def __init__(self, raw_conn): 61 | self._raw_conn = raw_conn 62 | self._pool = MySQLConnectionPool() 63 | 64 | def close(self): 65 | self._pool.release(self._raw_conn) 66 | 67 | def __getattr__(self, key): 68 | print("call method", key) 69 | if key == "close": 70 | return self.close 71 | else: 72 | return getattr(self._raw_conn, key) 73 | 74 | 75 | def connect(*args, **kwargs): 76 | pool = MySQLConnectionPool() 77 | raw_conn = pool.get_conn() 78 | return ConnectionProxy(raw_conn) 79 | 80 | def patch_pymysql(): 81 | sys.modules["pymysql"].connect = connect 82 | -------------------------------------------------------------------------------- /gtornado/redis.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | from __future__ import absolute_import 3 | import socket 4 | from redis.connection import Connection, ConnectionPool 5 | from gtornado import AsyncSocket 6 | 7 | __all__ = ("RedisConnectionPool", ) 8 | 9 | class AsyncRedisConnection(Connection): 10 | 11 | def _connect(self): 12 | try: 13 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 14 | iostream = AsyncSocket(sock) 15 | iostream.set_nodelay(True) 16 | iostream.connect((self.host, self.port), self.socket_connect_timeout) 17 | return iostream 18 | except: 19 | if sock is not None: 20 | iostream.close() 21 | 22 | 23 | class RedisConnectionPool(ConnectionPool): 24 | def __init__(self, **conn_args): 25 | conn_args.update({"connection_class": AsyncRedisConnection}) 26 | super(RedisConnectionPool, self).__init__(**conn_args) 27 | -------------------------------------------------------------------------------- /gtornado/requests.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | from __future__ import absolute_import 3 | import sys 4 | import socket 5 | from gtornado.green import AsyncSocket 6 | 7 | 8 | def create_connection(address, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, 9 | source_address=None, socket_options=None): 10 | try: 11 | host, port = address 12 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 13 | iostream = AsyncSocket(sock) 14 | iostream.connect((host, port)) 15 | iostream.set_nodelay(True) 16 | return iostream 17 | except: 18 | if sock is not None: 19 | iostream.close() 20 | sock = iostream = None 21 | 22 | def patch_requests(): 23 | sys.modules["requests"].packages.urllib3.util.connection.create_connection = create_connection 24 | -------------------------------------------------------------------------------- /gtornado/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | class Singleton(type): 3 | def __call__(clazz, *args, **kwargs): 4 | if hasattr(clazz, "_instance"): 5 | return clazz._instance 6 | else: 7 | clazz._instance = super(Singleton, clazz).__call__(*args, **kwargs) 8 | return clazz._instance 9 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | from setuptools import setup, find_packages 3 | setup( 4 | name = "gTornado", 5 | version = "0.1", 6 | packages = find_packages(), 7 | install_requires = ['tornado>=4.3', 'greenlet', "pymemcache>=1.3.5", "PyMySQL>=0.6.7"], 8 | author = "alexzhang", 9 | author_email = "alex8224@gmail.com", 10 | description = "gTornado add greenify support to tornado. inspired by motor and gevent", 11 | license = "MIT", 12 | keywords = "tornado greenify async", 13 | url = "https://github.com/alex8224/gTornado", 14 | ) 15 | 16 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | # Test Requirement 2 | 1. pip install -r requirements.txt 3 | 2. import `test.sql` to db 4 | 3. vi test.py, modify `params` 5 | 6 | 7 | # Run 8 | ```python 9 | python test.py port 10 | ``` 11 | -------------------------------------------------------------------------------- /tests/orm_storm.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | from storm.locals import * 3 | 4 | class AddressBook(object): 5 | __storm_table__ = "address_book" 6 | 7 | id = Int(primary=True) 8 | home = Unicode() 9 | phone = Unicode() 10 | office = Unicode() 11 | 12 | class StormStore(object): 13 | def __init__(self): 14 | self._store = None 15 | 16 | def __enter__(self): 17 | uri = "mysql://root:powerall@10.86.11.116/mywork" 18 | database = create_database(uri) 19 | self._store = Store(database) 20 | return self._store 21 | 22 | def __exit__(self, a, b, c): 23 | if self._store: 24 | self._store.close() 25 | 26 | 27 | class AddressBookDao(object): 28 | 29 | @staticmethod 30 | def query_by_phone(phone): 31 | with StormStore() as store: 32 | addr_book = store.find(AddressBook, AddressBook.phone==phone).one() 33 | addressbook = {} 34 | addressbook["id"] = addr_book.id 35 | addressbook["phone"] = addr_book.phone 36 | addressbook["home"] = addr_book.home 37 | addressbook["office"] = addr_book.office 38 | return addressbook 39 | -------------------------------------------------------------------------------- /tests/requirements.txt: -------------------------------------------------------------------------------- 1 | storm==0.20 2 | redis==2.10.5 3 | pylibmc==1.5.0 4 | MySQL-python==1.2.5 5 | -------------------------------------------------------------------------------- /tests/test.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | import sys 3 | from tornado.ioloop import IOLoop 4 | from tornado.gen import coroutine 5 | from tornado.web import RequestHandler, Application 6 | from gtornado import green 7 | 8 | import pymysql 9 | pymysql.install_as_MySQLdb() 10 | 11 | from gtornado.mysql import patch_pymysql 12 | patch_pymysql() 13 | 14 | from gtornado.mysql import MySQLConnectionPool 15 | 16 | params = { 17 | "host":"10.86.11.116", 18 | "port":3306, 19 | "username":"root", 20 | "password":"123456", 21 | "db":"mywork"} 22 | 23 | ConnectionPool = MySQLConnectionPool(max_size=200, mysql_params=params) 24 | 25 | 26 | from orm_storm import AddressBookDao 27 | 28 | class OrmTestHandler(RequestHandler): 29 | @coroutine 30 | def get(self): 31 | result = yield green.spawn(AddressBookDao.query_by_phone, u"13800138000") 32 | self.write(dict(rows=result)) 33 | 34 | import test_pure_mysql 35 | class PureHandler(RequestHandler): 36 | @coroutine 37 | def get(self): 38 | result = yield green.spawn(test_pure_mysql.query) 39 | self.write(dict(row=result)) 40 | 41 | 42 | import test_memcache 43 | class MemCacheHandler(RequestHandler): 44 | @coroutine 45 | def get(self): 46 | result = yield green.spawn(test_memcache.test) 47 | self.write(result) 48 | 49 | class HelloWorldHandler(RequestHandler): 50 | def wait(self): 51 | return 1 52 | 53 | @coroutine 54 | def get(self): 55 | #yield green.spawn(self.wait) 56 | self.write("HelloWorld!") 57 | 58 | 59 | app = Application([ 60 | (r"/async/orm/pure", PureHandler), 61 | (r"/async/orm/storm", OrmTestHandler), 62 | (r"/async/memcache", MemCacheHandler), 63 | (r"/async/", HelloWorldHandler), 64 | ]) 65 | 66 | import tornado.httpserver 67 | http_server = tornado.httpserver.HTTPServer(app) 68 | http_server.listen(int(sys.argv[1])) 69 | main_ioloop = IOLoop.instance() 70 | main_ioloop.start() 71 | -------------------------------------------------------------------------------- /tests/test.sql: -------------------------------------------------------------------------------- 1 | /* 2 | SQLyog Ultimate v9.01 3 | MySQL - 5.6.27-0ubuntu0.14.04.1 4 | ********************************************************************* 5 | */ 6 | /*!40101 SET NAMES utf8 */; 7 | 8 | create table `address_book` ( 9 | `id` int (11), 10 | `phone` varchar (54), 11 | `home` varchar (300), 12 | `office` varchar (300) 13 | ); 14 | insert into `address_book` (`id`, `phone`, `home`, `office`) values('1','13800138000','changde','shenzhen'); 15 | insert into `address_book` (`id`, `phone`, `home`, `office`) values('3','111','abc','dde'); 16 | insert into `address_book` (`id`, `phone`, `home`, `office`) values('5','asdf','aa','sadfad'); 17 | -------------------------------------------------------------------------------- /tests/test_greenify.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | import sys 3 | import time 4 | import greenify 5 | greenify.greenify() 6 | import pylibmc 7 | from tornado.ioloop import IOLoop 8 | from tornado.gen import coroutine 9 | from tornado.web import RequestHandler, Application 10 | from gtornado import green 11 | 12 | greenify.patch_lib("/usr/lib/x86_64-linux-gnu/libmemcached.so") 13 | 14 | 15 | class MCPool(green.Pool): 16 | def create_raw_conn(self): 17 | return pylibmc.Client(["localhost"]) 18 | 19 | mcpool = MCPool(200) 20 | 21 | def call_mc(i): 22 | try: 23 | mc = mcpool.get_conn() 24 | mc.set("timestamp", str(time.time())) 25 | return mc.get_stats() 26 | finally: 27 | mcpool.release(mc) 28 | 29 | class Hello(RequestHandler): 30 | @coroutine 31 | def get(self): 32 | result = yield green.spawn(call_mc, 1) 33 | self.write(dict(rows=result)) 34 | 35 | if __name__ == "__main__": 36 | app = Application([(r"/async/memcache", Hello)]) 37 | app.listen(int(sys.argv[1])) 38 | IOLoop.instance().start() 39 | -------------------------------------------------------------------------------- /tests/test_handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | from tornado.ioloop import IOLoop 3 | from tornado.web import Application 4 | from gtornado import Handler 5 | import MySQLdb.constants 6 | import MySQLdb.converters 7 | import MySQLdb.cursors 8 | from storm.locals import * 9 | 10 | import greenify 11 | greenify.greenify() 12 | 13 | # green.enable_debug() 14 | assert greenify.patch_lib("/usr/lib/x86_64-linux-gnu/libmysqlclient.so") 15 | 16 | conn_params = { 17 | "host": "10.86.11.116", "port":3306, 18 | "user": "root", "passwd": "123456", 19 | "db": "mywork", "charset": "utf8" 20 | } 21 | 22 | 23 | 24 | columns = ("id", "phone", "home", "office") 25 | 26 | class AddressBook(object): 27 | __storm_table__ = "address_book" 28 | 29 | id = Int(primary=True) 30 | home = Unicode() 31 | phone = Unicode() 32 | office = Unicode() 33 | 34 | 35 | class TestHandler(Handler): 36 | 37 | def test_select(self): 38 | db = None 39 | try: 40 | db = MySQLdb.connect(**conn_params) 41 | db.autocommit(True) 42 | cursor = db.cursor() 43 | cursor.execute("select * from address_book") 44 | while True: 45 | result = cursor.fetchmany() 46 | if result: 47 | if result: 48 | yield dict(zip(columns, result[0])) 49 | else: 50 | break 51 | cursor.close() 52 | except: 53 | raise 54 | finally: 55 | if db: 56 | db.close 57 | 58 | def prepare(self): 59 | self.db = None 60 | uri = "mysql://root:123456@10.86.11.116/mywork" 61 | database = create_database(uri) 62 | self.db = Store(database) 63 | 64 | def get(self): 65 | # for row in self.test_select(): 66 | # print(row) 67 | addrbooks = self.db.find(AddressBook, AddressBook.phone==u'13800138000') 68 | for addrbook in addrbooks: 69 | print(addrbook.home) 70 | self.write("ok") 71 | 72 | def finish(self, chunk=None): 73 | if self.db: 74 | self.db.close() 75 | super(TestHandler, self).finish(chunk) 76 | 77 | app = Application([(r"/", TestHandler)]) 78 | app.listen(30001) 79 | IOLoop.instance().start() 80 | -------------------------------------------------------------------------------- /tests/test_memcache.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | from gtornado.memcache import MemCachePool 3 | servers = {"servers":("127.0.0.1", 11211)} 4 | memcache_pool = MemCachePool(200, servers) 5 | 6 | def test(): 7 | client = None 8 | try: 9 | client = memcache_pool.get_conn() 10 | client.set("info", {"name":"alex", "address":"shenzhen"}) 11 | return client.get("info") 12 | except: 13 | raise 14 | finally: 15 | if client: 16 | memcache_pool.release(client) 17 | -------------------------------------------------------------------------------- /tests/test_mysqldb.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | from tornado.ioloop import IOLoop 3 | from tornado.web import RequestHandler, Application 4 | from tornado.gen import coroutine 5 | from gtornado import green 6 | 7 | import MySQLdb.constants 8 | import MySQLdb.converters 9 | import MySQLdb.cursors 10 | 11 | import greenify 12 | greenify.greenify() 13 | 14 | # green.enable_debug() 15 | assert greenify.patch_lib("/usr/lib/x86_64-linux-gnu/libmysqlclient.so") 16 | conn_params = { 17 | "host": "10.86.11.116", "port":3306, 18 | "user": "root", "passwd": "123456", 19 | "db": "mywork", "charset": "utf8" 20 | } 21 | 22 | def test_select(): 23 | db = None 24 | try: 25 | db = MySQLdb.connect(**conn_params) 26 | db.autocommit(True) 27 | cursor = db.cursor() 28 | cursor.execute("select * from address_book") 29 | while True: 30 | result = cursor.fetchmany() 31 | if result: 32 | pass 33 | else: 34 | break 35 | cursor.close() 36 | except: 37 | raise 38 | finally: 39 | if db: 40 | db.close 41 | 42 | def test_concurrent_wait(): 43 | db = None 44 | try: 45 | db = MySQLdb.connect(**conn_params) 46 | cursor = db.cursor() 47 | cursor.execute("select sleep(1)") 48 | print("done") 49 | cursor.close() 50 | except: 51 | raise 52 | finally: 53 | if db: 54 | db.close() 55 | 56 | @coroutine 57 | def start(): 58 | yield [green.spawn(test_concurrent_wait) for _ in range(1000)] 59 | #yield [green.spawn(test_select) for _ in range(100)] 60 | import time 61 | time.sleep(10) 62 | 63 | class DbHandler(RequestHandler): 64 | @coroutine 65 | def get(self): 66 | yield green.spawn(test_select) 67 | self.write(file("/home/alex/index").read()) 68 | 69 | app = Application([(r"/", DbHandler)]) 70 | app.listen(30001) 71 | #IOLoop.instance().run_sync(start) 72 | IOLoop.instance().start() 73 | -------------------------------------------------------------------------------- /tests/test_pure_mysql.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | from gtornado.mysql import MySQLConnectionPool 3 | 4 | ConnectionPool = MySQLConnectionPool() 5 | columns = ("id", "phone", "home", "office") 6 | 7 | def query(): 8 | connection = None 9 | try: 10 | connection = ConnectionPool.get_conn() 11 | with connection.cursor() as cursor: 12 | sql = "select * from address_book" 13 | cursor.execute(sql) 14 | rs = [] 15 | while 1: 16 | result = cursor.fetchmany() 17 | if result: 18 | row = dict(zip(columns, result[0])) 19 | rs.append(row) 20 | else: 21 | break 22 | return rs 23 | except Exception as ex: 24 | print(ex) 25 | raise 26 | finally: 27 | if connection: 28 | ConnectionPool.release(connection) 29 | -------------------------------------------------------------------------------- /tests/test_redis.py: -------------------------------------------------------------------------------- 1 | from gtornado.redis import RedisConnectionPool 2 | from gtornado import green 3 | from redis import StrictRedis 4 | from tornado.ioloop import IOLoop 5 | from tornado.web import RequestHandler, Application 6 | from tornado.gen import coroutine 7 | 8 | #green.enable_debug() 9 | pool = RedisConnectionPool(host="localhost", port=18000, db=0) 10 | 11 | class SubHandler(RequestHandler): 12 | 13 | def listen(self, channel): 14 | client = StrictRedis(connection_pool=pool) 15 | pubsub = client.pubsub() 16 | try: 17 | pubsub.subscribe(channel) 18 | for item in pubsub.listen(): 19 | data = item["data"] 20 | if data != 1L: 21 | return data 22 | except: 23 | raise 24 | finally: 25 | pubsub.close() 26 | 27 | @coroutine 28 | def get(self): 29 | channel = self.get_argument("channel", "inbox") 30 | message = yield green.spawn(self.listen, channel, timeout=10) 31 | self.write(message) 32 | 33 | 34 | class IfBlockingHandler(RequestHandler): 35 | @coroutine 36 | def get(self): 37 | self.write("no blocking") 38 | 39 | 40 | class RedisTest(RequestHandler): 41 | def get_value(self): 42 | client = StrictRedis(connection_pool=pool) 43 | return client.get("name") 44 | 45 | @coroutine 46 | def get(self): 47 | name = yield green.spawn(self.get_value) 48 | self.write(name) 49 | 50 | def redis_test(i): 51 | client = StrictRedis(host="localhost", db=0, connection_pool=pool) 52 | client.set("name_i" + str(i+1), "alex" + str(i)) 53 | client.get("name") 54 | 55 | def sleep(): 56 | green.sleep(10) 57 | 58 | 59 | app = Application([ 60 | (r"/sub", SubHandler), 61 | (r"/name", RedisTest), 62 | (r"/ifblocking", IfBlockingHandler), 63 | ]) 64 | 65 | app.listen(30001) 66 | 67 | 68 | #IOLoop.current().run_sync(start) 69 | IOLoop.instance().start() 70 | -------------------------------------------------------------------------------- /tests/test_requests.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | import sys 3 | from tornado.ioloop import IOLoop 4 | from tornado.gen import coroutine 5 | from tornado.web import RequestHandler, Application 6 | from gtornado import green 7 | import requests 8 | from gtornado.requests import patch_requests 9 | patch_requests() 10 | 11 | green.set_resolver("tornado.platform.caresresolver.CaresResolver") 12 | 13 | class HelloWorldHandler(RequestHandler): 14 | def get_url(self, url): 15 | headers = {"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36"} 16 | return requests.get(url, headers=headers) 17 | 18 | @coroutine 19 | def get(self): 20 | response = yield green.spawn(self.get_url, "http://www.163.com") 21 | self.write(response.text) 22 | 23 | 24 | class IfBlockingHandler(RequestHandler): 25 | def get(self): 26 | self.write("always serving") 27 | 28 | 29 | app = Application([ 30 | (r"/", HelloWorldHandler), 31 | (r"/ifblocking", IfBlockingHandler), 32 | ]) 33 | 34 | app.listen(int(sys.argv[1])) 35 | IOLoop.instance().start() 36 | -------------------------------------------------------------------------------- /tests/test_travis.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | from __future__ import absolute_import 3 | from tornado.ioloop import IOLoop 4 | from tornado.gen import coroutine 5 | from gtornado import green 6 | import traceback 7 | 8 | def test_green_sleep(): 9 | green.sleep(1) 10 | 11 | def test_green_task(): 12 | return "done" 13 | 14 | def test_call_green_task(): 15 | result = test_green_task() 16 | green.sleep(1) 17 | return result 18 | 19 | def test_green_timeout(): 20 | try: 21 | timeout = green.Timeout(1) 22 | timeout.start() 23 | green.sleep(2) 24 | except green.TimeoutException: 25 | traceback.print_exc() 26 | finally: 27 | timeout.cancel() 28 | 29 | @coroutine 30 | def test(): 31 | yield green.spawn(test_green_task) 32 | yield green.spawn(test_green_sleep) 33 | yield green.spawn(test_call_green_task) 34 | yield green.spawn(test_green_timeout) 35 | 36 | 37 | IOLoop.instance().run_sync(test) 38 | --------------------------------------------------------------------------------