` by default.
+ prefs['prefer_module_from_imports'] = False
+
+ # If `True`, rope will transform a comma list of imports into
+ # multiple separate import statements when organizing
+ # imports.
+ prefs['split_imports'] = False
+
+ # If `True`, rope will sort imports alphabetically by module name
+ # instead of alphabetically by import statement, with from imports
+ # after normal imports.
+ prefs['sort_imports_alphabetically'] = False
+
+
+def project_opened(project):
+ """This function is called after opening the project"""
+ # Do whatever you like here!
diff --git a/.vscode/settings.json b/.vscode/settings.json
index d6b1940..32cbf34 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,3 +1,7 @@
// 将设置放入此文件中以覆盖默认值和用户设置。
{
+ "python.pythonPath": "E:\\Program Files (x86)\\Python\\python.exe",
+ "python.linting.enabled": true,
+ "python.linting.lintOnSave": false,
+ "python.linting.flake8Enabled": true
}
\ No newline at end of file
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 0000000..eab2057
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,23 @@
+{
+ // See https://go.microsoft.com/fwlink/?LinkId=733558
+ // for the documentation about the tasks.json format
+ "version": "0.1.0",
+ "command": "npm",
+ "isShellCommand": true,
+ "showOutput": "always",
+ "suppressTaskName": true,
+ "tasks": [
+ {
+ "taskName": "install",
+ "args": ["install"]
+ },
+ {
+ "taskName": "update",
+ "args": ["update"]
+ },
+ {
+ "taskName": "test",
+ "args": ["run", "test"]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/awesome-python3-webapp/www/__pycache__/coroweb.cpython-35.pyc b/awesome-python3-webapp/www/__pycache__/coroweb.cpython-35.pyc
index 94073f6..99489d4 100644
Binary files a/awesome-python3-webapp/www/__pycache__/coroweb.cpython-35.pyc and b/awesome-python3-webapp/www/__pycache__/coroweb.cpython-35.pyc differ
diff --git a/awesome-python3-webapp/www/__pycache__/handlers.cpython-35.pyc b/awesome-python3-webapp/www/__pycache__/handlers.cpython-35.pyc
index 47f48f3..b73f8a6 100644
Binary files a/awesome-python3-webapp/www/__pycache__/handlers.cpython-35.pyc and b/awesome-python3-webapp/www/__pycache__/handlers.cpython-35.pyc differ
diff --git a/awesome-python3-webapp/www/__pycache__/orm.cpython-35.pyc b/awesome-python3-webapp/www/__pycache__/orm.cpython-35.pyc
index 035a2fc..576a622 100644
Binary files a/awesome-python3-webapp/www/__pycache__/orm.cpython-35.pyc and b/awesome-python3-webapp/www/__pycache__/orm.cpython-35.pyc differ
diff --git a/awesome-python3-webapp/www/app.py b/awesome-python3-webapp/www/app.py
index 10d22e7..92cae8b 100644
--- a/awesome-python3-webapp/www/app.py
+++ b/awesome-python3-webapp/www/app.py
@@ -1,76 +1,58 @@
-import logging
-import asyncio
-import json
-import os
-import time
-from aiohttp import web
+import logging; logging.basicConfig(level=logging.INFO)
+
+import asyncio, os, json, time
from datetime import datetime
+
+from aiohttp import web
from jinja2 import Environment, FileSystemLoader
import orm
from coroweb import add_routes, add_static
-logging.basicConfig(level=logging.INFO)
-
-# def index(request):
-# return web.Response(body='你好'.encode(encoding='utf-8'),
-# headers={'Content-Type': 'text/html;charset=utf-8'})
-
-
def init_jinja2(app, **kw):
- logging.info('init jinja2....')
- options = dict(autoescape=kw.get('autoescape', True),
- block_start_string=kw.get('block_start_string', '{%'),
- block_end_string=kw.get('block_end_string', '%}'),
- variable_start_string=kw.get('variable_start_string', '{{'),
- variable_end_string=kw.get('variable_end_string', '}}'),
- auto_reload=kw.get('auto_reload', True))
+ logging.info('init jinja2...')
+ options = dict(
+ autoescape = kw.get('autoescape', True),
+ block_start_string = kw.get('block_start_string', '{%'),
+ block_end_string = kw.get('block_end_string', '%}'),
+ variable_start_string = kw.get('variable_start_string', '{{'),
+ variable_end_string = kw.get('variable_end_string', '}}'),
+ auto_reload = kw.get('auto_reload', True)
+ )
path = kw.get('path', None)
- # logging.info('set jinja2 template path: %s' % path)
if path is None:
- path = os.path.join(
- os.path.dirname(os.path.abspath(__file__)), 'templates')
+ path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'templates')
logging.info('set jinja2 template path: %s' % path)
env = Environment(loader=FileSystemLoader(path), **options)
filters = kw.get('filters', None)
if filters is not None:
for name, f in filters.items():
env.filters[name] = f
- app['__templates__'] = env
-
+ app['__templating__'] = env
-@asyncio.coroutine
-def logger_factory(app, handler):
- def logger(request):
+async def logger_factory(app, handler):
+ async def logger(request):
logging.info('Request: %s %s' % (request.method, request.path))
# await asyncio.sleep(0.3)
- return handler(request)
-
+ return (await handler(request))
return logger
-
-@asyncio.coroutine
-def data_factory(app, handler):
- def parse_data(request):
+async def data_factory(app, handler):
+ async def parse_data(request):
if request.method == 'POST':
if request.content_type.startswith('application/json'):
- request.__data__ = request.json()
+ request.__data__ = await request.json()
logging.info('request json: %s' % str(request.__data__))
- elif request.content_type.startswith(
- 'application/x-www-form-urlencoded'):
- request.__data__ = request.post()
+ elif request.content_type.startswith('application/x-www-form-urlencoded'):
+ request.__data__ = await request.post()
logging.info('request form: %s' % str(request.__data__))
- return handler(request)
-
+ return (await handler(request))
return parse_data
-
-@asyncio.coroutine
-def response_factory(app, handler):
- def response(request):
+async def response_factory(app, handler):
+ async def response(request):
logging.info('Response handler...')
- r = handler(request)
- logging.info(type(r))
+ r = await handler(request)
if isinstance(r, web.StreamResponse):
return r
if isinstance(r, bytes):
@@ -86,15 +68,11 @@ def response(request):
if isinstance(r, dict):
template = r.get('__template__')
if template is None:
- resp = web.Response(body=json.dumps(
- r, ensure_ascii=False,
- default=lambda o: o.__dict__).encode('utf-8'))
+ resp = web.Response(body=json.dumps(r, ensure_ascii=False, default=lambda o: o.__dict__).encode('utf-8'))
resp.content_type = 'application/json;charset=utf-8'
return resp
else:
- resp = web.Response(
- body=app['__templating__'].get_template(template).render(
- **r).encode('utf-8'))
+ resp = web.Response(body=app['__templating__'].get_template(template).render(**r).encode('utf-8'))
resp.content_type = 'text/html;charset=utf-8'
return resp
if isinstance(r, int) and r >= 100 and r < 600:
@@ -107,10 +85,8 @@ def response(request):
resp = web.Response(body=str(r).encode('utf-8'))
resp.content_type = 'text/plain;charset=utf-8'
return resp
-
return response
-
def datetime_filter(t):
delta = int(time.time() - t)
if delta < 60:
@@ -124,28 +100,18 @@ def datetime_filter(t):
dt = datetime.fromtimestamp(t)
return u'%s年%s月%s日' % (dt.year, dt.month, dt.day)
-
-@asyncio.coroutine
-def init(loop):
- orm.create_pool(loop=loop,
- host='localhost',
- post=3306,
- user='sa',
- password='P@ssw0rd',
- db='awesome')
- app = web.Application(loop=loop,
- middlewares=[
- logger_factory, response_factory
- ])
+async def init(loop):
+ await orm.create_pool(loop=loop, host='127.0.0.1', port=3306, user='sa', password='P@ssw0rd', db='awesome')
+ app = web.Application(loop=loop, middlewares=[
+ logger_factory, response_factory
+ ])
init_jinja2(app, filters=dict(datetime=datetime_filter))
add_routes(app, 'handlers')
add_static(app)
- # app.router.add_route("GET", "/", index)
- srv = yield from loop.create_server(app.make_handler(), '127.0.0.1', 9003)
- logging.info('Server started at http://127.0.0.1:9003')
+ srv = await loop.create_server(app.make_handler(), '127.0.0.1', 9000)
+ logging.info('server started at http://127.0.0.1:9000...')
return srv
-
loop = asyncio.get_event_loop()
loop.run_until_complete(init(loop))
-loop.run_forever()
+loop.run_forever()
\ No newline at end of file
diff --git a/awesome-python3-webapp/www/coroweb.py b/awesome-python3-webapp/www/coroweb.py
index 4487ebf..dd54ed3 100644
--- a/awesome-python3-webapp/www/coroweb.py
+++ b/awesome-python3-webapp/www/coroweb.py
@@ -10,6 +10,10 @@
def get(path):
+ '''
+ Define decorator @get('/path')
+ '''
+
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw):
@@ -23,6 +27,10 @@ def wrapper(*args, **kw):
def post(path):
+ '''
+ Define decorator @post('/path')
+ '''
+
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw):
@@ -71,7 +79,6 @@ def has_request_arg(fn):
sig = inspect.signature(fn)
params = sig.parameters
found = False
-
for name, param in params.items():
if name == 'request':
found = True
@@ -84,71 +91,9 @@ def has_request_arg(fn):
% (fn.__name__, str(sig)))
return found
-# class RequestHandler(object):
-# def __init__(self, app, fn):
-# self._app = app
-# self._func = fn
-# self._has_request_arg = has_request_arg(fn)
-# self._has_var_kw_arg = has_var_kw_arg(fn)
-# self._has_named_kw_args = has_named_kw_args(fn)
-# self._named_kw_args = get_named_kw_args(fn)
-# self._required_kw_args = get_required_kw_args(fn)
-#
-# def __call__(self, request):
-# kw = None
-# if self._has_var_kw_arg or self._has_named_kw_args or self._required_kw_args:
-# if request.method == 'POST':
-# if not request.content_type:
-# return web.HTTPBadRequest('Missing content-type')
-# ct = request.content_type.lower()
-# if ct.startswith('application/json'):
-# params = request.json()
-# if not isinstance(params, dict):
-# return web.HTTPBadRequest('JSON body must be object.')
-# kw = params
-# elif ct.startswith(
-# 'application/x-www-form-urlencoded') or ct.startswith(
-# 'multipart/form-data'):
-# params = request.post()
-# kw = dict(**params)
-# else:
-# return web.HTTPBadRequest('Unsupported Content-Type:%s' %
-# request.content_type)
-# if request.method == 'GET':
-# qs = request.quert_string
-# if qs:
-# kw = dict()
-# for k, v in parse.parse_qs(qs, True).items():
-# kw[k] = v[0]
-# if kw is None:
-# kw = dict(**request.match_info)
-# else:
-# if not self._has_var_kw_arg and self._named_kw_args:
-# copy = dict()
-# for name in self._named_kw_args:
-# if name in kw:
-# copy[name] = name
-# kw = copy
-# for k, v in request.match_info.items():
-# if k in kw:
-# logging.warning(
-# 'Duplicate arg name in named arg and kw args: %s' % k)
-# kw[k] = v
-# if self._has_request_arg:
-# kw['request'] = request
-# if self._required_kw_args:
-# for name in self._required_kw_args:
-# if name not in kw:
-# return web.HTTPBadRequest('Missing argument:%s' % name)
-# logging.info('Call with args :%s ' % str(kw))
-# try:
-# r = self._func(**kw)
-# return r
-# except Exception as e:
-# return dict(error=e.error, data=e.data, message=e.message)
-
class RequestHandler(object):
+ logging.info('RequestHandler Start....')
def __init__(self, app, fn):
self._app = app
self._func = fn
@@ -158,6 +103,9 @@ def __init__(self, app, fn):
self._named_kw_args = get_named_kw_args(fn)
self._required_kw_args = get_required_kw_args(fn)
+ logging.info('RequestHandler init .... ')
+ logging.info('object %s' % str(object))
+
async def __call__(self, request):
kw = None
if self._has_var_kw_arg or self._has_named_kw_args or self._required_kw_args:
@@ -170,14 +118,11 @@ async def __call__(self, request):
if not isinstance(params, dict):
return web.HTTPBadRequest('JSON body must be object.')
kw = params
- elif ct.startswith(
- 'application/x-www-form-urlencoded') or ct.startswith(
- 'multipart/form-data'):
+ elif ct.startswith('application/x-www-form-urlencoded') or ct.startswith('multipart/form-data'):
params = await request.post()
kw = dict(**params)
else:
- return web.HTTPBadRequest('Unsupported Content-Type: %s' %
- request.content_type)
+ return web.HTTPBadRequest('Unsupported Content-Type: %s' % request.content_type)
if request.method == 'GET':
qs = request.query_string
if qs:
@@ -197,52 +142,52 @@ async def __call__(self, request):
# check named arg:
for k, v in request.match_info.items():
if k in kw:
- logging.warning(
- 'Duplicate arg name in named arg and kw args: %s' % k)
+ logging.warning('Duplicate arg name in named arg and kw args: %s' % k)
kw[k] = v
if self._has_request_arg:
kw['request'] = request
# check required kw:
if self._required_kw_args:
for name in self._required_kw_args:
- if not name in kw:
+ if name not in kw:
return web.HTTPBadRequest('Missing argument: %s' % name)
logging.info('call with args: %s' % str(kw))
try:
r = await self._func(**kw)
return r
- except APIError as e:
+ except Exception as e:
return dict(error=e.error, data=e.data, message=e.message)
def add_static(app):
path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'static')
app.router.add_static('/static/', path)
- logging.info('add static %s => %s ' % ('/static/', path))
+ logging.info('add static %s => %s' % ('/static/', path))
def add_route(app, fn):
method = getattr(fn, '__method__', None)
path = getattr(fn, '__route__', None)
+ logging.info('path:%s,method:%s' % (path,method))
if path is None or method is None:
raise ValueError('@get or @post not defined in %s.' % str(fn))
- if not asyncio.iscoroutinefunction(fn) and not inspect.signature(fn):
+ if not asyncio.iscoroutinefunction(fn) and not inspect.isgeneratorfunction(fn):
fn = asyncio.coroutine(fn)
logging.info('add route %s %s => %s(%s)' %
(method, path, fn.__name__,
', '.join(inspect.signature(fn).parameters.keys())))
+ # logging.info('method: %s,fn: %s' % (method,fn))
app.router.add_route(method, path, RequestHandler(app, fn))
-def add_routes(app, moudle_name):
- n = moudle_name.rfind('.')
- # logging.info(' n %s ' % str(n))
+def add_routes(app, module_name):
+ n = module_name.rfind('.')
if n == (-1):
- mod = __import__(moudle_name, globals(), locals())
+ mod = __import__(module_name, globals(), locals())
else:
- name = moudle_name[n + 1:]
+ name = module_name[n + 1:]
mod = getattr(
- __import__(moudle_name[:n], globals(), locals(), [name]), name)
+ __import__(module_name[:n], globals(), locals(), [name]), name)
for attr in dir(mod):
if attr.startswith('_'):
continue
diff --git a/awesome-python3-webapp/www/handlers.py b/awesome-python3-webapp/www/handlers.py
index 3ba13dd..f3f6833 100644
--- a/awesome-python3-webapp/www/handlers.py
+++ b/awesome-python3-webapp/www/handlers.py
@@ -1,18 +1,25 @@
-import re
-import time
-import json
-import logging
-import hashlib
-import base64
-import asyncio
+import re, time, json, logging, hashlib, base64, asyncio
from coroweb import get, post
from models import User, Comment, Blog, next_id
+# @get('/')
+# async def index(request):
+# logging.info('users')
+# users = await User.findAll()
+# return {'__template__': 'test.html', 'users': users}
+#
+#
+# @get('/test')
+# async def index(request):
+# logging.info('test')
+# users = await User.findAll()
+# return {'__template__': 'test.html', 'users': users}
+
+# 测试
@get('/')
-def index(request):
- logging.info('test User.findAll()')
- users = User.findAll()
+async def index(request):
+ users = await User.findAll()
return {'__template__': 'test.html', 'users': users}
diff --git a/awesome-python3-webapp/www/orm.py b/awesome-python3-webapp/www/orm.py
index c5a54ff..8b33dd5 100644
--- a/awesome-python3-webapp/www/orm.py
+++ b/awesome-python3-webapp/www/orm.py
@@ -9,54 +9,60 @@
logging.basicConfig(level=logging.INFO)
-@asyncio.coroutine
-def create_pool(loop, **kw):
+async def create_pool(loop, **kw):
logging.info('create database connection pool...')
global __pool
- __pool = yield from aiomysql.create_pool(
- host=kw.get('host', 'localhost'),
- port=kw.get('port', 3306),
- user=kw['user'],
- password=kw['password'],
- db=kw['db'],
- charset=kw.get('charset', 'utf8'),
- autocommit=kw.get('autocommit', True),
- maxsize=kw.get('maxsize', 10),
- minsize=kw.get('minsize', 1),
- loop=loop)
+ __pool = await aiomysql.create_pool(host=kw.get('host', 'localhost'),
+ port=kw.get('port', 3306),
+ user=kw['user'],
+ password=kw['password'],
+ db=kw['db'],
+ charset=kw.get('charset', 'utf8'),
+ autocommit=kw.get('autocommit', True),
+ maxsize=kw.get('maxsize', 10),
+ minsize=kw.get('minsize', 1),
+ loop=loop)
def log(sql, args=()):
logging.info('SQL: %s' % sql)
-@asyncio.coroutine
-def select(sql, args, size=None):
+async def select(sql, args, size=None):
log(sql, args)
- global __pool
- with (yield from __pool) as conn:
- cur = yield from conn.cursor(aiomysql.DictCursor)
- yield from cur.execute(sql.replace('?', '%s'), args or ())
- if size:
- rs = yield from cur.fetchmany(size)
- else:
- rs = yield from cur.fetchall()
- yield from cur.close()
- logging.info('rows returned %s' % len(rs))
- return rs
-
-
-def execute(sql, args):
- log(sql)
- with (yield from __pool) as conn:
+ async with __pool.get() as conn:
+ # 等待连接对象返回DictCursor可以通过dict的方式获取数据库对象,需要通过游标对象执行SQL
+ async with conn.cursor(aiomysql.DictCursor) as cur:
+ await cur.execute(
+ sql.replace('?', '%s'),
+ args) #将sql中的'?'替换为'%s',因为mysql语句中的占位符为%s
+ #如果传入size'
+ if size:
+ resultset = await cur.fetchmany(size) # 从数据库获取指定的行数
+ else:
+ resultset = await cur.fetchall() # 返回所有的结果集
+ logging.info('rows returned: %s' % len(resultset))
+ return resultset
+
+
+async def execute(sql, args, autocommit=True):
+ log(sql, args)
+ async with __pool.get() as conn:
+ if not autocommit: # 若数据库的事务为非自动提交的,则调用协程启动连接
+ await conn.begin()
try:
- cur = yield from conn.cursor()
- yield from cur.execute(sql.replace('?', '%s'), args)
- affected = cur.rowcount
- yield from cur.close()
- except Exception as e:
- raise
+ async with conn.cursor(
+ aiomysql.
+ DictCursor) as cur: # 打开一个DictCursor,它与普通游标的不同在于,以dict形式返回结果
+ await cur.execute(sql.replace('?', '%s'), args)
+ affected = cur.rowcount # 返回受影响的行数
+ if not autocommit: # 同上, 事务非自动提交型的,手动调用协程提交增删改事务
+ await conn.commit()
+ except BaseException as e:
+ if not autocommit: # 出错, 回滚事务到增删改之前
+ await conn.rollback()
+ raise e
return affected
@@ -163,7 +169,7 @@ def __getattr__(self, key):
try:
return self[key]
except KeyError:
- raise AttributeError(r'Model object has no attribute %s' % key)
+ raise AttributeError(r"'Model' object has no attribute '%s'" % key)
def __setattr__(self, key, value):
self[key] = value
@@ -184,27 +190,29 @@ def getValueOrDefault(self, key):
return value
@classmethod
- @asyncio.coroutine
- def find(cls, pk):
+ async def find(cls, pk):
logging.info('find object by primary key')
- rs = yield from select('%s where `%s`=?',
- (cls.__select__, cls.__primary_key__), [pk], 1)
+ rs = await select('%s where `%s`=?' %
+ (cls.__select__, cls.__primary_key__), [pk], 1)
if len(rs) == 0:
return None
return cls(**rs[0])
- @asyncio.coroutine
- def save(self):
+ @classmethod
+ async def save(self):
args = list(map(self.getValueOrDefault, self.__fields__))
args.append(self.getValueOrDefault(self.__primary_key__))
logging.info(args)
- rows = yield from execute(self.__insert__, args)
+ rows = await execute(self.__insert__, args)
if rows != 1:
logging.warn('failed to insert record: affected rows: %s' % rows)
- @asyncio.coroutine
- def findAll(cls, where=None, args=None, **kw):
+ @classmethod
+ async def findAll(cls, where=None, args=None, **kw):
+ ' find objects by where clause. '
sql = [cls.__select__]
+ # logging.info(cls.__select__)
+ logging.info(select(' '.join(sql), args))
if where:
sql.append('where')
sql.append(where)
@@ -225,5 +233,5 @@ def findAll(cls, where=None, args=None, **kw):
args.extend(limit)
else:
raise ValueError('Invalid limit value: %s' % str(limit))
- rs = select(' '.join(sql), args)
+ rs = await select(' '.join(sql), args)
return [cls(**r) for r in rs]
diff --git a/awesome-python3-webapp/www/templates/test.html b/awesome-python3-webapp/www/templates/test.html
index f23da43..939356a 100644
--- a/awesome-python3-webapp/www/templates/test.html
+++ b/awesome-python3-webapp/www/templates/test.html
@@ -6,9 +6,7 @@
- All users
{% for u in users %}
- {{ u.name }} / {{ u.email }}
- {% endfor %}
+ My name is chenshi2