├── .gitignore ├── LICENSE ├── README.md └── bottle-py-v0.4.10 ├── bottle.py ├── bottle_v0.4.10_annotated.pdf ├── bottle_v0.4.10_annotated.png └── readme.rst /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | *.egg-info/ 23 | .installed.cfg 24 | *.egg 25 | 26 | # PyInstaller 27 | # Usually these files are written by a python script from a template 28 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 29 | *.manifest 30 | *.spec 31 | 32 | # Installer logs 33 | pip-log.txt 34 | pip-delete-this-directory.txt 35 | 36 | # Unit test / coverage reports 37 | htmlcov/ 38 | .tox/ 39 | .coverage 40 | .coverage.* 41 | .cache 42 | nosetests.xml 43 | coverage.xml 44 | *,cover 45 | 46 | # Translations 47 | *.mo 48 | *.pot 49 | 50 | # Django stuff: 51 | *.log 52 | 53 | # Sphinx documentation 54 | docs/_build/ 55 | 56 | # PyBuilder 57 | target/ 58 | 59 | # Numerous always-ignore extensions 60 | *.diff 61 | *.err 62 | *.orig 63 | *.log 64 | *.rej 65 | *.swo 66 | *.swp 67 | *.vi 68 | *~ 69 | *.sass-cache 70 | *.pyc 71 | 72 | # OS or Editor folders 73 | .DS_Store 74 | .cache 75 | .idea 76 | .project 77 | .settings 78 | .tmproj 79 | .module-cache 80 | nbproject 81 | Thumbs.db 82 | docs 83 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 hhstore 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 | # 特别提醒 ! ! ! 2 | 3 | - 本项目全部内容, 已经迁移到: [hhstore/annotated-py-projects](https://github.com/hhstore/annotated-py-projects) 4 | - 请访问新项目对应子目录学习, 谢谢. 5 | - 本 repo 已废弃, 不在更新 ! ! ! 6 | -------------------------------------------------------------------------------- /bottle-py-v0.4.10/bottle.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | ############################################################################### 4 | # 代码注释说明 5 | # 6 | # Annotated_Author: hhstore 7 | # Email: selfrebuild@gmail.com 8 | # Date: 2015-08 9 | # 10 | ############################################################################### 11 | 12 | 13 | 14 | """ 15 | Bottle is a fast and simple mirco-framework for small web-applications. It 16 | offers request dispatching (Routes) with url parameter support, Templates, 17 | key/value Databases, a build-in HTTP Server? and adapters for many third party 18 | WSGI/HTTP-server and template engines. All in a single file and with no 19 | dependencies other than the Python Standard Library. 20 | 21 | Homepage and documentation: http://wiki.github.com/defnull/bottle 22 | 23 | Special thanks to Stefan Matthias Aust [http://github.com/sma] 24 | for his contribution to SimpelTemplate 25 | 26 | 27 | Example 28 | ------- 29 | 30 | from bottle import route, run, request, response, send_file, abort 31 | 32 | @route('/') 33 | def hello_world(): 34 | return 'Hello World!' 35 | 36 | @route('/hello/:name') 37 | def hello_name(name): 38 | return 'Hello %s!' % name 39 | 40 | @route('/hello', method='POST') 41 | def hello_post(): 42 | name = request.POST['name'] 43 | return 'Hello %s!' % name 44 | 45 | @route('/static/:filename#.*#') 46 | def static_file(filename): 47 | send_file(filename, root='/path/to/static/files/') 48 | 49 | run(host='localhost', port=8080) 50 | 51 | """ 52 | 53 | 54 | 55 | 56 | 57 | __author__ = 'Marcel Hellkamp' 58 | __version__ = '0.4.10' 59 | __license__ = 'MIT' 60 | 61 | ############################################################################### 62 | # ########################## 依赖导包 ############################### 63 | ############################################################################### 64 | import cgi 65 | import mimetypes 66 | import os 67 | import os.path 68 | import sys 69 | import traceback 70 | import re 71 | import random 72 | import Cookie 73 | import threading # 关键依赖 74 | import time 75 | 76 | try: 77 | from urlparse import parse_qs 78 | except ImportError: 79 | from cgi import parse_qs 80 | try: 81 | import cPickle as pickle 82 | except ImportError: 83 | import pickle 84 | try: 85 | import anydbm as dbm 86 | except ImportError: 87 | import dbm 88 | 89 | 90 | 91 | 92 | 93 | 94 | ############################################################################### 95 | ############################################################################### 96 | ############################################################################### 97 | 98 | # Exceptions and Events 异常处理 和 事件处理 定义部分. 99 | 100 | class BottleException(Exception): # 异常处理-基类 101 | """ A base class for exceptions used by bottle.""" 102 | pass 103 | 104 | 105 | class HTTPError(BottleException): # HTTP 请求错误. 106 | """ A way to break the execution and instantly jump to an error handler. """ 107 | 108 | def __init__(self, status, text): 109 | self.output = text 110 | self.http_status = int(status) 111 | 112 | def __str__(self): 113 | return self.output 114 | 115 | 116 | class BreakTheBottle(BottleException): # 中断 bottle 服务 117 | """ Not an exception, but a straight jump out of the controller code. 118 | 119 | Causes the WSGIHandler to instantly call start_response() and return the 120 | content of output """ 121 | 122 | def __init__(self, output): 123 | self.output = output 124 | 125 | 126 | class TemplateError(BottleException): # 模板错误 127 | """ Thrown by template engines during compilation of templates """ 128 | pass 129 | 130 | 131 | 132 | 133 | ############################################################################### 134 | ############################################################################### 135 | ############################################################################### 136 | 137 | 138 | 139 | 140 | # WSGI abstraction: Request and response management 141 | # 请求request 和响应 response 管理. 142 | def WSGIHandler(environ, start_response): 143 | """The bottle WSGI-handler.""" 144 | global request # 引用全局变量 145 | global response 146 | 147 | request.bind(environ) 148 | response.bind() 149 | ############################################################################### 150 | 151 | try: 152 | handler, args = match_url(request.path, request.method) # 调用,下面定义. 153 | if not handler: 154 | raise HTTPError(404, "Not found") 155 | output = handler(**args) 156 | except BreakTheBottle, shard: 157 | output = shard.output 158 | except Exception, exception: 159 | response.status = getattr(exception, 'http_status', 500) 160 | errorhandler = ERROR_HANDLER.get(response.status, error_default) 161 | try: 162 | output = errorhandler(exception) 163 | except: 164 | output = "Exception within error handler! Application stopped." 165 | 166 | if response.status == 500: 167 | request._environ['wsgi.errors'].write("Error (500) on '%s': %s\n" % (request.path, exception)) 168 | 169 | db.close() # DB cleanup 170 | 171 | ############################################################################### 172 | 173 | if hasattr(output, 'read'): 174 | fileoutput = output 175 | if 'wsgi.file_wrapper' in environ: 176 | output = environ['wsgi.file_wrapper'](fileoutput) 177 | else: 178 | output = iter(lambda: fileoutput.read(8192), '') 179 | elif isinstance(output, str): 180 | output = [output] 181 | 182 | ############################################################################### 183 | 184 | for c in response.COOKIES.values(): 185 | response.header.add('Set-Cookie', c.OutputString()) # 响应头. 186 | 187 | # finish 188 | status = '%d %s' % (response.status, HTTP_CODES[response.status]) 189 | start_response(status, list(response.header.items())) # 关键代码 190 | return output 191 | 192 | 193 | ############################################################################### 194 | ############################################################################### 195 | ############################################################################### 196 | # 针对threading.local() 说明: 197 | # - 1. 表示thread-local数据的一个类 198 | # - 2. thread-local数据是值只特定于线程的数据 199 | # - 3. 要管理thread-local数据,只需创建local(或其子类)的一个实例并在它上面存储属性 200 | # - 4. 该实例的值对于各自的线程将是不同的。 201 | ############################################################################### 202 | 203 | # 请求-管理类. 204 | class Request(threading.local): 205 | """ Represents a single request using thread-local namespace. """ 206 | 207 | def bind(self, environ): # 请求,绑定 208 | """ Binds the enviroment of the current request to this request handler """ 209 | self._environ = environ 210 | self._GET = None 211 | self._POST = None 212 | self._GETPOST = None 213 | self._COOKIES = None 214 | self.path = self._environ.get('PATH_INFO', '/').strip() 215 | if not self.path.startswith('/'): 216 | self.path = '/' + self.path 217 | 218 | @property 219 | def method(self): # 请求类型: GET,POST 等. 220 | ''' Returns the request method (GET,POST,PUT,DELETE,...) ''' 221 | return self._environ.get('REQUEST_METHOD', 'GET').upper() 222 | 223 | @property 224 | def query_string(self): 225 | """ Content of QUERY_STRING """ 226 | return self._environ.get('QUERY_STRING', '') 227 | 228 | @property 229 | def input_length(self): 230 | ''' Content of CONTENT_LENGTH ''' 231 | try: 232 | return int(self._environ.get('CONTENT_LENGTH', '0')) 233 | except ValueError: 234 | return 0 235 | 236 | ############################################################################### 237 | 238 | @property 239 | def GET(self): # GET 请求. 240 | """Returns a dict with GET parameters.""" 241 | if self._GET is None: 242 | raw_dict = parse_qs(self.query_string, keep_blank_values=1) 243 | self._GET = {} 244 | for key, value in raw_dict.items(): 245 | if len(value) == 1: 246 | self._GET[key] = value[0] 247 | else: 248 | self._GET[key] = value 249 | return self._GET 250 | 251 | @property 252 | def POST(self): # POST 请求. 253 | """Returns a dict with parsed POST data.""" 254 | if self._POST is None: 255 | raw_data = cgi.FieldStorage(fp=self._environ['wsgi.input'], environ=self._environ) 256 | self._POST = {} 257 | if raw_data: 258 | for key in raw_data: 259 | if isinstance(raw_data[key], list): 260 | self._POST[key] = [v.value for v in raw_data[key]] 261 | elif raw_data[key].filename: 262 | self._POST[key] = raw_data[key] 263 | else: 264 | self._POST[key] = raw_data[key].value 265 | return self._POST 266 | 267 | @property 268 | def params(self): 269 | ''' Returns a mix of GET and POST data. POST overwrites GET ''' 270 | if self._GETPOST is None: 271 | self._GETPOST = dict(self.GET) # 调用GET() 272 | self._GETPOST.update(dict(self.POST)) # 调用POST() 273 | return self._GETPOST 274 | 275 | @property 276 | def COOKIES(self): 277 | """Returns a dict with COOKIES.""" 278 | if self._COOKIES is None: 279 | raw_dict = Cookie.SimpleCookie(self._environ.get('HTTP_COOKIE', '')) # 调用 280 | self._COOKIES = {} 281 | for cookie in raw_dict.values(): 282 | self._COOKIES[cookie.key] = cookie.value 283 | return self._COOKIES 284 | 285 | 286 | 287 | # 响应-管理类 288 | class Response(threading.local): 289 | """ Represents a single response using thread-local namespace. """ 290 | 291 | def bind(self): 292 | """ Clears old data and creates a brand new Response object """ 293 | self._COOKIES = None 294 | 295 | self.status = 200 296 | self.header = HeaderDict() # HTTP 头数据 297 | self.content_type = 'text/html' 298 | self.error = None 299 | 300 | @property 301 | def COOKIES(self): 302 | if not self._COOKIES: 303 | self._COOKIES = Cookie.SimpleCookie() 304 | return self._COOKIES 305 | 306 | def set_cookie(self, key, value, **kargs): 307 | """ Sets a Cookie. Optional settings: expires, path, comment, domain, max-age, secure, version, httponly """ 308 | self.COOKIES[key] = value 309 | for k in kargs: 310 | self.COOKIES[key][k] = kargs[k] 311 | 312 | def get_content_type(self): 313 | '''Gives access to the 'Content-Type' header and defaults to 'text/html'.''' 314 | return self.header['Content-Type'] 315 | 316 | def set_content_type(self, value): 317 | self.header['Content-Type'] = value 318 | 319 | content_type = property(get_content_type, set_content_type, None, get_content_type.__doc__) 320 | 321 | 322 | 323 | ############################################################################### 324 | ############################################################################### 325 | 326 | # 头字典-管理类 327 | class HeaderDict(dict): 328 | ''' A dictionary with case insensitive (titled) keys. 329 | 330 | You may add a list of strings to send multible headers with the same name.''' 331 | 332 | def __setitem__(self, key, value): 333 | return dict.__setitem__(self, key.title(), value) 334 | 335 | def __getitem__(self, key): 336 | return dict.__getitem__(self, key.title()) 337 | 338 | def __delitem__(self, key): 339 | return dict.__delitem__(self, key.title()) 340 | 341 | def __contains__(self, key): 342 | return dict.__contains__(self, key.title()) 343 | 344 | def items(self): 345 | """ Returns a list of (key, value) tuples """ 346 | for key, values in dict.items(self): 347 | if not isinstance(values, list): 348 | values = [values] # 类型转换 349 | for value in values: 350 | yield (key, str(value)) 351 | 352 | def add(self, key, value): 353 | """ Adds a new header without deleting old ones """ 354 | if isinstance(value, list): 355 | for v in value: 356 | self.add(key, v) 357 | elif key in self: 358 | if isinstance(self[key], list): 359 | self[key].append(value) 360 | else: 361 | self[key] = [self[key], value] 362 | else: 363 | self[key] = [value] 364 | 365 | 366 | 367 | 368 | 369 | 370 | ############################################################################### 371 | # 异常处理部分 372 | ############################################################################### 373 | 374 | 375 | # HTTP状态码: 500 376 | # 处理: 抛出异常 377 | def abort(code=500, text='Unknown Error: Appliction stopped.'): 378 | """ Aborts execution and causes a HTTP error. """ 379 | raise HTTPError(code, text) 380 | 381 | 382 | # HTTP状态码: 307 383 | # 处理: 抛出异常, 重定向 384 | def redirect(url, code=307): 385 | """ Aborts execution and causes a 307 redirect """ 386 | response.status = code # 响应 - 状态码 387 | response.header['Location'] = url # 响应 - 头 388 | 389 | raise BreakTheBottle("") # 抛出异常 390 | 391 | 392 | # HTTP状态码: 401 393 | # 异常处理: 发送一个静态文本,作相应 394 | def send_file(filename, root, guessmime=True, mimetype='text/plain'): 395 | """ Aborts execution and sends a static files as response. """ 396 | root = os.path.abspath(root) + '/' 397 | filename = os.path.normpath(filename).strip('/') 398 | filename = os.path.join(root, filename) 399 | 400 | if not filename.startswith(root): # HTTP状态码: 401 401 | abort(401, "Access denied.") 402 | if not os.path.exists(filename) or not os.path.isfile(filename): 403 | abort(404, "File does not exist.") # 文件不存在 404 | if not os.access(filename, os.R_OK): 405 | abort(401, "You do not have permission to access this file.") 406 | 407 | if guessmime: 408 | guess = mimetypes.guess_type(filename)[0] 409 | if guess: 410 | response.content_type = guess 411 | elif mimetype: 412 | response.content_type = mimetype 413 | elif mimetype: 414 | response.content_type = mimetype 415 | 416 | stats = os.stat(filename) 417 | # TODO: HTTP_IF_MODIFIED_SINCE -> 304 (Thu, 02 Jul 2009 23:16:31 CEST) 418 | if 'Content-Length' not in response.header: 419 | response.header['Content-Length'] = stats.st_size 420 | if 'Last-Modified' not in response.header: 421 | ts = time.gmtime(stats.st_mtime) 422 | ts = time.strftime("%a, %d %b %Y %H:%M:%S +0000", ts) 423 | response.header['Last-Modified'] = ts 424 | 425 | raise BreakTheBottle(open(filename, 'r')) # 抛出异常 426 | 427 | 428 | ############################################################################### 429 | ############################################################################### 430 | ############################################################################### 431 | 432 | 433 | # Routing 路由处理部分-定义 434 | 435 | def compile_route(route): # 编译路由串 436 | """ Compiles a route string and returns a precompiled RegexObject. 437 | 438 | Routes may contain regular expressions with named groups to support url parameters. 439 | Example: '/user/(?P[0-9]+)' will match '/user/5' with {'id':'5'} 440 | 441 | A more human readable syntax is supported too. 442 | Example: '/user/:id/:action' will match '/user/5/kiss' with {'id':'5', 'action':'kiss'} 443 | """ 444 | route = route.strip().lstrip('$^/ ').rstrip('$^ ') # 字符串过滤字符. 445 | 446 | route = re.sub(r':([a-zA-Z_]+)(?P[^\w/])(?P.+?)(?P=uniq)', r'(?P<\1>\g)', route) 447 | route = re.sub(r':([a-zA-Z_]+)', r'(?P<\1>[^/]+)', route) 448 | 449 | return re.compile('^/%s$' % route) # 路由需要正则表达式处理. 450 | 451 | 452 | ############################################################################### 453 | # 功能: URL 匹配 454 | # 455 | # 参数: 456 | # - url: 路由地址 457 | # - method: 请求的方法, GET, POST 等 458 | ############################################################################### 459 | def match_url(url, method='GET'): # 匹配 URL 地址 --- 在 WSGIHandler() 函数中调用. 460 | """Returns the first matching handler and a parameter dict or (None, None). 461 | 462 | This reorders the ROUTING_REGEXP list every 1000 requests. To turn this off, use OPTIMIZER=False""" 463 | 464 | url = '/' + url.strip().lstrip("/") # URL 串过滤字符. 465 | 466 | # 路由匹配: 467 | # 先从全局的静态路由表里查找,是否已经存在. 468 | # 469 | # Search for static routes first 470 | route = ROUTES_SIMPLE.get(method, {}).get(url, None) # 第一次搜索静态路由. 471 | 472 | if route: 473 | return (route, {}) # 找到静态路由,直接返回. 474 | 475 | # 如果未找到,搜索匹配 476 | # 477 | # Now search regexp routes 478 | routes = ROUTES_REGEXP.get(method, []) # 没找到,搜索 路由的正则表达式串. 479 | 480 | for i in xrange(len(routes)): 481 | match = routes[i][0].match(url) 482 | if match: 483 | handler = routes[i][1] 484 | 485 | if i > 0 and OPTIMIZER and random.random() <= 0.001: 486 | # Every 1000 requests, we swap the matching route with its predecessor. 487 | # Frequently used routes will slowly wander up the list. 488 | routes[i - 1], routes[i] = routes[i], routes[i - 1] # 交换 489 | 490 | return (handler, match.groupdict()) # 返回处理结果. 491 | return (None, None) # 处理失败,返回 None 492 | 493 | 494 | ############################################################################### 495 | # 功能: 添加路由至路由映射表 496 | # 497 | # 参数: 498 | # - route: 路由 499 | # - handler: 500 | # - method: 501 | # - simple: 502 | # 结果: 更新2个全局路由字典 503 | # - ROUTES_SIMPLE 504 | # - ROUTES_REGEXP 505 | # 506 | def add_route(route, handler, method='GET', simple=False): 507 | """ Adds a new route to the route mappings. 508 | 509 | Example: 510 | def hello(): 511 | return "Hello world!" 512 | add_route(r'/hello', hello)""" 513 | method = method.strip().upper() # 对请求参数,作统一格式化 514 | 515 | if re.match(r'^/(\w+/)*\w*$', route) or simple: # 正则匹配路由 516 | ROUTES_SIMPLE.setdefault(method, {})[route] = handler # 更新全局路由字典 517 | else: 518 | route = compile_route(route) # 调用, 定义在前面. 519 | ROUTES_REGEXP.setdefault(method, []).append([route, handler]) # 更新全局路由字典 520 | 521 | 522 | ############################################################################### 523 | # 功能: 路由装饰器 524 | # 525 | # 参数: 526 | # - url: 527 | # 528 | # 依赖: 529 | # - 包裹函数: add_route() 530 | # 531 | def route(url, **kargs): 532 | """ Decorator for request handler. Same as add_route(url, handler).""" 533 | 534 | def wrapper(handler): 535 | add_route(url, handler, **kargs) 536 | return handler 537 | 538 | return wrapper 539 | 540 | 541 | ############################################################################### 542 | 543 | def validate(**vkargs): # 数据安全校验函数.---- 写成多层装饰器,技巧代码 544 | ''' Validates and manipulates keyword arguments by user defined callables 545 | and handles ValueError and missing arguments by raising HTTPError(400) ''' 546 | 547 | def decorator(func): # 装饰器 548 | def wrapper(**kargs): # 包裹函数 549 | for key in vkargs: 550 | if key not in kargs: 551 | abort(403, 'Missing parameter: %s' % key) 552 | try: 553 | kargs[key] = vkargs[key](kargs[key]) 554 | except ValueError, e: 555 | abort(403, 'Wrong parameter format for: %s' % key) 556 | return func(**kargs) # 注意返回值 557 | 558 | return wrapper # 调用 559 | 560 | return decorator # 调用 561 | 562 | 563 | ############################################################################### 564 | 565 | # Error handling 出错处理部分定义. 566 | 567 | def set_error_handler(code, handler): # 错误的辅助处理函数.被下面 error()调用. 568 | """ Sets a new error handler. """ 569 | code = int(code) # 状态码 提示信息.将传入参数,转换成整型值. 570 | ERROR_HANDLER[code] = handler # ERROR_HANDLER{}本身是个全局字典,这里整型值作键,填入 value 值. 571 | 572 | 573 | ############################################################################### 574 | 575 | def error(code=500): # 出错处理 -- 写成装饰器函数. 576 | """ Decorator for error handler. Same as set_error_handler(code, handler).""" 577 | 578 | def wrapper(handler): # 包裹函数. 579 | set_error_handler(code, handler) 580 | return handler 581 | 582 | return wrapper # 调用 内嵌包裹函数. 583 | 584 | 585 | ############################################################################### 586 | ######################## 服务适配器部分 ########################################## 587 | # 1. web Server 这部分代码,多是导入现成的包,自己修改处理的代码,很少. 588 | # 2. 注意这种开发思想. 589 | # 3. 这里有 内嵌函数定义的应用,注意一下. 590 | ############################################################################### 591 | 592 | # Server adapter 服务适配器部分-定义 593 | # 由全局的run()函数, 定位到此处. 594 | class ServerAdapter(object): 595 | def __init__(self, host='127.0.0.1', port=8080, **kargs): 596 | self.host = host 597 | self.port = int(port) 598 | self.options = kargs 599 | 600 | def __repr__(self): 601 | return "%s (%s:%d)" % (self.__class__.__name__, self.host, self.port) 602 | 603 | def run(self, handler): 604 | pass 605 | 606 | 607 | ############################################################################### 608 | # 接口定义.调用. 609 | # mark 610 | class WSGIRefServer(ServerAdapter): # 不同的 web 服务器,导入不同的包处理.并重写run()函数. 611 | def run(self, handler): # 重写 run() 函数. 612 | from wsgiref.simple_server import make_server 613 | 614 | srv = make_server(self.host, self.port, handler) # 调用其他人写的库,所以这个代码,自己处理的内容很少. 615 | srv.serve_forever() # 开启服务. 616 | 617 | 618 | class CherryPyServer(ServerAdapter): 619 | def run(self, handler): 620 | from cherrypy import wsgiserver 621 | 622 | server = wsgiserver.CherryPyWSGIServer((self.host, self.port), handler) 623 | server.start() 624 | 625 | 626 | class FlupServer(ServerAdapter): 627 | def run(self, handler): # 重写 run() 函数. 628 | from flup.server.fcgi import WSGIServer 629 | 630 | WSGIServer(handler, bindAddress=(self.host, self.port)).run() 631 | 632 | 633 | class PasteServer(ServerAdapter): 634 | def run(self, handler): # 重写 run() 函数. 635 | from paste import httpserver 636 | from paste.translogger import TransLogger 637 | 638 | app = TransLogger(handler) 639 | httpserver.serve(app, host=self.host, port=str(self.port)) 640 | 641 | 642 | class FapwsServer(ServerAdapter): 643 | """ Extreamly fast Webserver using libev (see http://william-os4y.livejournal.com/) 644 | Experimental ... """ 645 | 646 | def run(self, handler): # 重写 run() 函数. 647 | import fapws._evwsgi as evwsgi 648 | from fapws import base 649 | import sys 650 | 651 | evwsgi.start(self.host, self.port) 652 | evwsgi.set_base_module(base) 653 | 654 | def app(environ, start_response): # 函数嵌套定义,特别注意. 655 | environ['wsgi.multiprocess'] = False 656 | return handler(environ, start_response) 657 | 658 | evwsgi.wsgi_cb(('', app)) # 调用内嵌的 app()函数 659 | evwsgi.run() 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | ############################################################################### 668 | # 框架全局入口 669 | ############################################################################### 670 | # 功能: 全局入口 671 | # 672 | # 参数: 673 | # - server: web服务器 674 | # - host: 访问IP 675 | # - port: 端口号 676 | # - optinmize: 性能优化开关(该单词拼写有误) 677 | # - kargs: 扩展参数 678 | # 679 | # 关键代码: 680 | # - server.run(WSGIHandler) # 启动web服务 681 | # 备注: 682 | # 1. 注意run()函数默认参数选项. 683 | # 2. server.run() 根据不同的 web Server ,进行选择. 684 | # 3. 理解 bottle 库源码的组织结构. 685 | # 686 | ############################################################################### 687 | 688 | def run(server=WSGIRefServer, host='127.0.0.1', port=8080, optinmize=False, **kargs): 689 | """ Runs bottle as a web server, using Python's built-in wsgiref implementation by default. 690 | 691 | You may choose between WSGIRefServer, CherryPyServer, FlupServer and 692 | PasteServer or write your own server adapter. 693 | """ 694 | global OPTIMIZER 695 | 696 | OPTIMIZER = bool(optinmize) 697 | quiet = bool('quiet' in kargs and kargs['quiet']) # 传入该参数,运行后,不输出提示log信息 698 | 699 | # 对 server 参数作检查: 若 server 参数是类名, 进行一次 实例化 操作. 700 | # Instanciate server, if it is a class instead of an instance 701 | if isinstance(server, type) and issubclass(server, ServerAdapter): 702 | server = server(host=host, port=port, **kargs) # 初始化服务参数. 703 | 704 | # 再次检查 server 参数 705 | if not isinstance(server, ServerAdapter): # 若处理后的 server, 并非 ServerAdapter 的 实例,报错 706 | raise RuntimeError("Server must be a subclass of ServerAdapter") 707 | 708 | if not quiet: 709 | print 'Bottle server starting up (using %s)...' % repr(server) 710 | print 'Listening on http://%s:%d/' % (server.host, server.port) 711 | print 'Use Ctrl-C to quit.' 712 | print 713 | 714 | try: 715 | server.run(WSGIHandler) # 启动web服务. 关键参数是 server 参数. 是 ServerAdapter() 类的实例. 716 | except KeyboardInterrupt: 717 | print "Shuting down..." 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | ############################################################################### 727 | # 异常处理 728 | ############################################################################### 729 | 730 | # Templates异常处理 731 | class TemplateError(BottleException): pass 732 | 733 | 734 | class TemplateNotFoundError(BottleException): pass 735 | 736 | 737 | ############################################################################### 738 | 739 | class BaseTemplate(object): # 模板的基类定义. 740 | def __init__(self, template='', filename='