`
什么都不懂的孩子
  • 浏览: 26723 次
社区版块
存档分类
最新评论

python 编程规范

 
阅读更多

程序模板

Python代码  收藏代码
  1. @FileName:  
  2.  
  3. @Author:xx@ic.net.cn  
  4.  
  5. @Create date:  
  6.  
  7. @description:用一行文字概述模块或脚本,用句号结尾。  
  8.  
  9. @Update date:  
  10.  
  11. @Vindicator: xx@ic.net.cn  
  12.  
  13. @File URL: http://idea.icgoo.net/xxxxxxx  
  14.  
  15. @svn Path: svn://svn.icinfo.net/xxxxxx  
  16.   
  17. """  
  18.   
  19. #add by XXX  or  modify by XXX  
  20. def Function(parameter1 ,parameter2...):  
  21. ''''' 
  22.     @description: 
  23.     @parameter1: 
  24.     @parameter2: 
  25.     @... 
  26.     @return: 
  27. '''  

 

 

基本原则

  1. 方便代码的交流和维护.
  2. 不影响编码的效率,不与大众习惯冲突.
  3. 使代码更美观,阅读更方便.
  4. 使代码的逻辑更清晰,更易于理解.

 

编码

  1.   *所有的 Python 脚本文件都应在文件头标上如下标识或其兼容格式的标识
  2.   *设置编辑器,默认保存为 utf-8 格式
  3.   *不论什么情况使用 UTF-8 吧!这是王道!

  # -*- coding:utf-8 -*-   或  #coding=utf-8

 

 

命名

  1.   一致的命名可以给开发人员减少许多麻烦,而恰如其分的命名则可以大幅提高代码的可读性,降低维护成本.
  2.   Python库的命名约定有点混乱,所以我们将永远不能使之变得完全一致--- 不过还是有公认的命名规范的. 
  3.   新的模块和包(包括第三方的框架)必须符合这些标准,但对已有的库存在不同风格的, 保持内部的一致性是首选的

  *一些特殊的字符要避免.如小写字母'l','o'

 

模块名

  1.   模块应该是不含下划线的,简短的,小写的名字.

  例:module.py

 

  2.    对于包内使用的模块,可以加一个下划线前缀.

  例:_internal_module.py

 

类名

  1.   几乎没有例外,类名总是使用首字母大写单词串(CapWords)的约定.
  2.   不使用下划线连接单词,也不加入 C、T 等前缀

  例:

  class ThisIsAClass(object):  

      pass  

 

函数名

  1.  函数名全部小写,由下划线连接各个单词
  2.  类似mixedCase函数名仅被允许用于这种风格已经占优势的上下文(如: threading.py) 以便保持向后兼容. 

  例:

  def this_is_a_func(self):

      pass

 

变量名

  1.   变量名全部小写,由下划线连接各个单词
  2.   不论是类成员变量还是全局变量,均不使用 m 或 g 前缀
  3.   私有类成员使用单一下划线前缀标识,多定义公开成员,少定义私有成员。
  4.   变量名不应带有类型信息,因为 Python 是动态类型语言。如 iValue、names_list、dict_obj 等都是不好的命名。

  例:

  color = WHITE  

  this_is_a_variable = 1

 

常量名

  1.   常量名所有字母大写,由下划线连接各个单词

  例:

  WHITE = 0xffffffff  

  THIS_IS_A_CONSTANT = 1 

 

异常名

  1.   如果模块对所有情况定义了单个异常,它通常被叫做"error"或"Error"
  2.   似乎内建(扩展)的模块使用"error"(例如:os.error), 而Python模块通常用"Error" (例如: xdrlib.Error).
  3.   趋势是使用(CapWords)

 

缩写

   1.    命名应当尽量使用全拼写的单词

 

   2.    常用的缩写,如 XML、ID等,在命名时也应只大写首字母

       例:class XmlParser(object):pass 

 

   3.    命名中含有长单词,对某个单词进行缩写

       例:function 缩写为 fn;text 缩写为 txt;object 缩写为 obj等

 

特殊命名

   1.    用下划线作前导或结尾的特殊形式是被公认的

        _single_leading_underscore(以一个下划线作前导): 弱的"内部使用(internal use)"标志

       例:  "from M import *"不会导入以下划线开头的对象

 

   2.    single_trailing_underscore_(以一个下划线结尾): 用于避免与Python关键词的冲突

       例:"Tkinter.Toplevel(master, class_='ClassName')"

 

   3.    __double_leading_underscore(双下划线): 从Python 1.4起为类私有名

 

   4.    __double_leading_and_trailing_underscore__: 特殊的(magic) 对象或属性,存在于用户控制的(user-controlled)名字空间

       例:__init__, __import__ 或 __file__

 

缩进

  1.    使用制表符还是空格?

  -- 永远不要混用制表符和空格.建议使用空格.

       -- 我们内部应该都是使用的4个空格的tab.

 

空行

  适当的空行有利于增加代码的可读性,加空行可以参考如下几个准则

 

  1.    在类、函数的定义间加空行

      -- 用两行空行分割顶层函数和类的定义,类内方法的定义用单个空行分割. 

 

  2.    额外的空行可被用于分割一组相关函数

 

  3.    在 import 不同种类的模块间加空行

 

  4.    在函数中的逻辑段落间加空行,即把相关的代码紧凑写在一起,作为一个逻辑段落,段落间以空行分隔

 

空格

  1.   空格在 Python 代码中是有意义的,因为 Python 的语法依赖于缩进,在行首的空格称为前导空格.这里不谈这个.
  2.   非前导空格在 Python 代码中没有意义,但适当地加入非前导空格可以增进代码的可读性。

   3.    在二元算术、逻辑运算符前后加空格

        例:a = b + c(好)  a=b+c(不好)

 

   4.    “:”用在行尾时前后皆不加空格,如分枝、循环、函数和类定义语言;用在非行尾时两端加空格,如 dict 对象的定义

        例:d = {'key' : 'value'}(好) d = {'key':'value'}(不好)

 

   5.    括号(含圆括号、方括号和花括号)前后不加空格

        例:do_something(arg1, arg2)(好) do_something( arg1, arg2 )(不好)

 

   6.    逗号后面加一个空格,前面不加空格

        例:print x, y(好) print x , y(不好)

 

   7.    函数调用,索引切片,字典取值不要用空格

        例:spam(x)(好) spam (x)(不好); list[i](好) list [i](不好); dict[key](好) dict [key](不好)

 

断行

    *尽管现在的宽屏显示器已经可以单屏显示超过 256 列字符,但本规范仍然坚持行的最大长度不得超过 78 个字符的标准

 

   1.    为长变量名换一个短名

        例: this._is.a.very.long.variable_name = this._is.another.long.variable_name (不好)

        variable_name = this._is.another.long.variable_name (好)

 

   2.    在括号(包括圆括号、方括号和花括号)内换行

    例: 

      class Edit(Widget):

          def __init__(self, parent, width,

              font = FONT, color = BLACK, pos = POS, style = 0): # 注意:多一层缩进

              pass

 

    如果行长到连第一个括号内的参数都放不下,则每个元素都单独占一行

    例:

      very_very_very_long_variable_name = ui.widgets.Edit(

          panrent,

          width,

          font,

          color,

          pos) # 注意:多一层缩进

      do_sth_with(very_very_very_long_variable_name)

 

   3.    在长行加入续行符强行断行,断行的位置应在操作符前,且换行后多一个缩进,以使维护人员看代码的时候看到代码行首即可判定这里存在换行

    例:

      if color == WHITE or color == BLACK \

              or color == BLUE: # 注意 or 操作符在新行的行首而不是旧行的行尾,上一行的续行符不可省略

          do_something(color);

      else:

          do_something(DEFAULT_COLOR);

 

导入

  1.   Imports 通常被放置在文件的顶部,仅在模块注释和文档字符串之后,在模块的全局变量和常量之前.Imports应该有顺序地成组安放
  2.   对于内部包的导入是不推荐使用相对导入的.对所有导入都要使用包的绝对路径
  3.   import应该按照从最常用到最不常用的顺序分组放置,这几种模块中用空行分隔开来

     import标准库

     import第三方库

     importGoogle App Engine 相关库

     importDjango 框架相关库

     importSoC framework 相关库

     import基于 SoC 框架的模块

     import应用程序特有的内容 

  例:

     import a_standard

     import b_standard

     import a_third_party

     import b_third_party

 

     from a_soc import f

     from a_soc import g

 

     import a_soc

     import b_soc

 

  1.单行导入

  例: 

    import x

    import y

    import x, y(不好)

  *如果是from xx import xx可以一行

  例: from types import StringType, ListType

 

  2.从一个包含类的模块中导入类时

  例:

     from MyClass import MyClass

     from foo.bar.YourClass import YourClass

  如果发生地名字冲突,可以

     import MyClass

     import foo.bar.YourClass

  然后再按下面使用即可

     MyClass.MyClass

     foo.bar.YourClass.YourClass

 

注释

  1.   ** 业界普遍认同 Python 的注释分为两种的概念,一种是由 # 开头的“真正的”注释,另一种是 docstrings。
  2.   ** 前者表明为何选择当前实现以及这种实现的原理和难点,后者表明如何使用这个包、模块、类、函数(方法),甚至包括使用示例和单元测试。
  3.   ** 坚持适当注释原则。对不存在技术难点的代码坚持不注释,对存在技术难点的代码必须注释
  4.   ** 推荐对每一个包、模块、类、函数(方法)写 docstrings,除非代码一目了然,非常简单
  5.   ** 包、模块、类、函数的第一个语句如果是字符串那么就是一个 __doc__ String。

 

文件注释

  ** 每个文件开头都应该包含一个带有版权信息和许可声明的块注释。 

Python代码  收藏代码
  1. """ 
  2.     用一行文字概述模块或脚本,用句号结尾。 
  3.     留一个空行。本 __doc__ string 的其他部分应该包括模块或脚本的全面描述。作为可选项,还可以包括导出的类和函数的简要描述。 
  4.  
  5.   ClassFoo: 一行概述。 
  6.   functionBar(): 一行概述。 
  7. """  
  8.   
  9. __authors__ = [  
  10.   # 请按照姓氏字母顺序排列:  
  11.   '"John Smith" <johnsmith@example.com>',  
  12.   '"Joe Paranoid" <joeisgone@example.com>',  # 应提供电子邮件地址  
  13. ]  

 

类注释

  ** 类应该在描述它的类定义下面放 __doc__ string

 

Python代码  收藏代码
  1. class SampleClass(object):  
  2.   
  3.   """这里是类的概述。 
  4.  
  5.   详细的描述信息…… 
  6.   详细的描述信息…… 
  7.  
  8.   Attributes: 
  9.     likes_spam: 布尔型,表示我们是否喜欢垃圾邮件。 
  10.     eggs: 整数,数我们下了多少蛋。 
  11.   """  
  12.   
  13.   def __init__(self, likes_spam=False):  
  14.     """拿点什么来初始化 SampleClass 。 
  15.  
  16.     Args: 
  17.       likes_spam: 初始化指标,表示 SampleClass 实例是否喜欢垃圾邮件(默认是 False)。 
  18.     """  
  19.     self.likes_spam = likes_spam  
  20.     self.eggs = 0  
  21.   
  22.   def publicMethod(self):  
  23.     """执行一些操作。"""  
  24.     pass  

 

 

 

函数注释

  ** 如果不是用途非常明显而且非常短的话,所有函数和方法都应该有 __doc__ string 

  ** 所有外部能访问的函数和方法,无论有多短、有多简单,都应该有 __doc__ string 

Python代码  收藏代码
  1. def fetchRows(table, keys):  
  2.   """取出表中的多行内容。 
  3.  
  4.   Args: 
  5.     table: 打开的表。 Table 类的实例。 
  6.     keys: 字符串序列,表示要从表中取出的行的键值。 
  7.  
  8.   Returns: 
  9.     一个字典,映射指定键值与取出的表中对应行的数据: 
  10.  
  11.     {'Serak': ('Rigel VII', 'Preparer'), 
  12.      'Zim': ('Irk', 'Invader'), 
  13.      'Lrrr': ('Omicron Persei 8', 'Emperor')} 
  14.  
  15.     如果 keys 参数中的键值没有出现在字典里,就表示对应行在表中没找到。 
  16.  
  17.   Raises: 
  18.     IOError: 访问 table.Table 对象时发生的错误。 
  19.   """  
  20.   pass  

 

 

单行注释

  1.   ** 加注释的最后一个位置是在难以理解的代码里面
  2.   ** 如果你打算在下一次代码复查(code review)的时候解释这是什么意思,那你应该现在就把它写成注释。
  3.   ** 在开始进行操作之前,就应该给复杂的操作写几行注释
  4.   ** 对不直观的代码则应该在行末写注释

  # 我们用带权的字典检索来查找 i 在数组中的位置。我们根据数组中最大的数和

  # 数组的长度来推断可能的位置,然后做二分法检索找到准确的值。

 

  if i & (i-1) == 0:        # 当且仅当 i 是 2 的幂时,值为 true

 

TODO style

  1.   ** 在代码中使用 TODO 注释是临时的、短期的解决方案,或者说是足够好但不够完美的办法
  2.   ** TODO 应该包括全部大写的字符串 TODO ,紧接用圆括号括起来的你的用户名

  # TODO(someuser): 这里应该用 "*" 来做级联操作。

  # TODO(anotheruser) 用 relations 来修改这儿。

 

赋值

  ** 对于赋值语句,主要是不要做无谓的对齐

  ** 原因有两点:一是这种对齐会打乱编程时的注意力,大脑要同时处理两件事(编程和对齐);

               二是以后阅读和维护都很困难,因为人眼的横向视野很窄,把三个字段看成一行很困难,而且维护时要增加一个更长的变量名也会破坏对齐

 

    例:

      x        = 1

      y        = 1

      long_var = 1 (不好)

 

      x = 1

      y = 1

      long_var = 1 (好)

 

分支与循环

Python代码  收藏代码
  1. ** 不要写成一行  
  2.   例:  
  3.     if not flg: pass (不好)  
  4.     if not flg:    
  5.         pass         (好)  
  6.   
  7.     for i in xrange(10): print i  (不好)  
  8.     for i in xrange(10):    
  9.         print i                   (好)  
  10.   
  11. ** 条件表达式的编写应该足够 pythonic,如以下形式的条件表达式是拙劣的  
  12.   例:  
  13.     if len(alist) != 0: do_something()  
  14.     if alist != []: do_something()  
  15.     if s != "": do_something()  
  16.     if var != None: do_something()  
  17.     if var != False: do_something()  
  18.   
  19.   这样写就好了:  
  20.     if alist: do_something()  
  21.     if s: do_something()  
  22.     if var: do_something()  

 

 

文档

这个东西可以另外FILE来讲了

 

调试

写道
所有程序,如果需要加入调试输出打印代码。 

需要在源程序的 import 包之后,加入一个变量 用大写名称 DEBUG 变量,即 DEBUG=True 

后面的打印语句加上 if DEBUG: print 'hello world'

在准备交付生产环境时,提交版本库的时候,必须关闭调试开关,也就是 DEBUG变量修改成 DEBUG=False

 

 

例子

Python代码  收藏代码
  1. """ 
  2. BaseHTTPServer that implements the Python WSGI protocol (PEP 333, rev 1.21). 
  3.  
  4. Adapted from wsgiref.simple_server: http://svn.eby-sarna.com/wsgiref/ 
  5.  
  6. This is a simple server for use in testing or debugging Django apps. It hasn't 
  7. been reviewed for security issues. Don't use it for production use. 
  8. """  
  9.   
  10. from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer  
  11. import os  
  12. import re  
  13. import socket  
  14. import sys  
  15. import urllib  
  16. import warnings  
  17.   
  18. from django.core.management.color import color_style  
  19. from django.utils.http import http_date  
  20. from django.utils._os import safe_join  
  21. from django.views import static  
  22.   
  23. from django.contrib.staticfiles import handlers  
  24.   
  25. __version__ = "0.1"  
  26. __all__ = ['WSGIServer','WSGIRequestHandler']  
  27.   
  28. server_version = "WSGIServer/" + __version__  
  29. sys_version = "Python/" + sys.version.split()[0]  
  30. software_version = server_version + ' ' + sys_version  
  31.   
  32. class WSGIServerException(Exception):  
  33.     pass  
  34.   
  35. class FileWrapper(object):  
  36.     """Wrapper to convert file-like objects to iterables"""  
  37.   
  38.     def __init__(self, filelike, blksize=8192):  
  39.         self.filelike = filelike  
  40.         self.blksize = blksize  
  41.         if hasattr(filelike,'close'):  
  42.             self.close = filelike.close  
  43.   
  44.     def __getitem__(self,key):  
  45.         data = self.filelike.read(self.blksize)  
  46.         if data:  
  47.             return data  
  48.         raise IndexError  
  49.   
  50.     def __iter__(self):  
  51.         return self  
  52.   
  53.     def next(self):  
  54.         data = self.filelike.read(self.blksize)  
  55.         if data:  
  56.             return data  
  57.         raise StopIteration  
  58.   
  59. # Regular expression that matches `special' characters in parameters, the  
  60. # existence of which force quoting of the parameter value.  
  61. tspecials = re.compile(r'[ \(\)<>@,;:\\"/\[\]\?=]')  
  62.   
  63. def _formatparam(param, value=None, quote=1):  
  64.     """Convenience function to format and return a key=value pair. 
  65.  
  66.     This will quote the value if needed or if quote is true. 
  67.     """  
  68.     if value is not None and len(value) > 0:  
  69.         if quote or tspecials.search(value):  
  70.             value = value.replace('\\', '\\\\').replace('"', r'\"')  
  71.             return '%s="%s"' % (param, value)  
  72.         else:  
  73.             return '%s=%s' % (param, value)  
  74.     else:  
  75.         return param  
  76.   
  77. class Headers(object):  
  78.     """Manage a collection of HTTP response headers"""  
  79.     def __init__(self,headers):  
  80.         if not isinstance(headers, list):  
  81.             raise TypeError("Headers must be a list of name/value tuples")  
  82.         self._headers = headers  
  83.   
  84.     def __len__(self):  
  85.         """Return the total number of headers, including duplicates."""  
  86.         return len(self._headers)  
  87.   
  88.     def __setitem__(self, name, val):  
  89.         """Set the value of a header."""  
  90.         del self[name]  
  91.         self._headers.append((name, val))  
  92.   
  93.     def __delitem__(self,name):  
  94.         """Delete all occurrences of a header, if present. 
  95.  
  96.         Does *not* raise an exception if the header is missing. 
  97.         """  
  98.         name = name.lower()  
  99.         self._headers[:] = [kv for kv in self._headers if kv[0].lower()<>name]  
  100.   
  101.     def __getitem__(self,name):  
  102.         """Get the first header value for 'name' 
  103.  
  104.         Return None if the header is missing instead of raising an exception. 
  105.  
  106.         Note that if the header appeared multiple times, the first exactly which 
  107.         occurrance gets returned is undefined.  Use getall() to get all 
  108.         the values matching a header field name. 
  109.         """  
  110.         return self.get(name)  
  111.   
  112.     def has_key(self, name):  
  113.         """Return true if the message contains the header."""  
  114.         return self.get(name) is not None  
  115.   
  116.     __contains__ = has_key  
  117.   
  118.     def get_all(self, name):  
  119.         """Return a list of all the values for the named field. 
  120.  
  121.         These will be sorted in the order they appeared in the original header 
  122.         list or were added to this instance, and may contain duplicates.  Any 
  123.         fields deleted and re-inserted are always appended to the header list. 
  124.         If no fields exist with the given name, returns an empty list. 
  125.         """  
  126.         name = name.lower()  
  127.         return [kv[1for kv in self._headers if kv[0].lower()==name]  
  128.   
  129.   
  130.     def get(self,name,default=None):  
  131.         """Get the first header value for 'name', or return 'default'"""  
  132.         name = name.lower()  
  133.         for k,v in self._headers:  
  134.             if k.lower()==name:  
  135.                 return v  
  136.         return default  
  137.   
  138.     def keys(self):  
  139.         """Return a list of all the header field names. 
  140.  
  141.         These will be sorted in the order they appeared in the original header 
  142.         list, or were added to this instance, and may contain duplicates. 
  143.         Any fields deleted and re-inserted are always appended to the header 
  144.         list. 
  145.         """  
  146.         return [k for k, v in self._headers]  
  147.   
  148.     def values(self):  
  149.         """Return a list of all header values. 
  150.  
  151.         These will be sorted in the order they appeared in the original header 
  152.         list, or were added to this instance, and may contain duplicates. 
  153.         Any fields deleted and re-inserted are always appended to the header 
  154.         list. 
  155.         """  
  156.         return [v for k, v in self._headers]  
  157.   
  158.     def items(self):  
  159.         """Get all the header fields and values. 
  160.  
  161.         These will be sorted in the order they were in the original header 
  162.         list, or were added to this instance, and may contain duplicates. 
  163.         Any fields deleted and re-inserted are always appended to the header 
  164.         list. 
  165.         """  
  166.         return self._headers[:]  
  167.   
  168.     def __repr__(self):  
  169.         return "Headers(%s)" % `self._headers`  
  170.   
  171.     def __str__(self):  
  172.         """str() returns the formatted headers, complete with end line, 
  173.         suitable for direct HTTP transmission."""  
  174.         return '\r\n'.join(["%s: %s" % kv for kv in self._headers]+['',''])  
  175.   
  176.     def setdefault(self,name,value):  
  177.         """Return first matching header value for 'name', or 'value' 
  178.  
  179.         If there is no header named 'name', add a new header with name 'name' 
  180.         and value 'value'."""  
  181.         result = self.get(name)  
  182.         if result is None:  
  183.             self._headers.append((name,value))  
  184.             return value  
  185.         else:  
  186.             return result  
  187.   
  188.     def add_header(self, _name, _value, **_params):  
  189.         """Extended header setting. 
  190.  
  191.         _name is the header field to add.  keyword arguments can be used to set 
  192.         additional parameters for the header field, with underscores converted 
  193.         to dashes.  Normally the parameter will be added as key="value" unless 
  194.         value is None, in which case only the key will be added. 
  195.  
  196.         Example: 
  197.  
  198.         h.add_header('content-disposition', 'attachment', filename='bud.gif') 
  199.  
  200.         Note that unlike the corresponding 'email.Message' method, this does 
  201.         *not* handle '(charset, language, value)' tuples: all values must be 
  202.         strings or None. 
  203.         """  
  204.         parts = []  
  205.         if _value is not None:  
  206.             parts.append(_value)  
  207.         for k, v in _params.items():  
  208.             if v is None:  
  209.                 parts.append(k.replace('_''-'))  
  210.             else:  
  211.                 parts.append(_formatparam(k.replace('_''-'), v))  
  212.         self._headers.append((_name, "; ".join(parts)))  
  213.   
  214. def guess_scheme(environ):  
  215.     """Return a guess for whether 'wsgi.url_scheme' should be 'http' or 'https' 
  216.     """  
  217.     if environ.get("HTTPS"in ('yes','on','1'):  
  218.         return 'https'  
  219.     else:  
  220.         return 'http'  
  221.   
  222. _hop_headers = {  
  223.     'connection':1'keep-alive':1'proxy-authenticate':1,  
  224.     'proxy-authorization':1'te':1'trailers':1'transfer-encoding':1,  
  225.     'upgrade':1  
  226. }  
  227.   
  228. def is_hop_by_hop(header_name):  
  229.     """Return true if 'header_name' is an HTTP/1.1 "Hop-by-Hop" header"""  
  230.     return header_name.lower() in _hop_headers  
  231.   
  232. class ServerHandler(object):  
  233.     """Manage the invocation of a WSGI application"""  
  234.   
  235.     # Configuration parameters; can override per-subclass or per-instance  
  236.     wsgi_version = (1,0)  
  237.     wsgi_multithread = True  
  238.     wsgi_multiprocess = True  
  239.     wsgi_run_once = False  
  240.   
  241.     origin_server = True    # We are transmitting direct to client  
  242.     http_version  = "1.0"   # Version that should be used for response  
  243.     server_software = software_version  
  244.   
  245.     # os_environ is used to supply configuration from the OS environment:  
  246.     # by default it's a copy of 'os.environ' as of import time, but you can  
  247.     # override this in e.g. your __init__ method.  
  248.     os_environ = dict(os.environ.items())  
  249.   
  250.     # Collaborator classes  
  251.     wsgi_file_wrapper = FileWrapper     # set to None to disable  
  252.     headers_class = Headers             # must be a Headers-like class  
  253.   
  254.     # Error handling (also per-subclass or per-instance)  
  255.     traceback_limit = None  # Print entire traceback to self.get_stderr()  
  256.     error_status = "500 INTERNAL SERVER ERROR"  
  257.     error_headers = [('Content-Type','text/plain')]  
  258.   
  259.     # State variables (don't mess with these)  
  260.     status = result = None  
  261.     headers_sent = False  
  262.     headers = None  
  263.     bytes_sent = 0  
  264.   
  265.     def __init__(self, stdin, stdout, stderr, environ, multithread=True,  
  266.         multiprocess=False):  
  267.         self.stdin = stdin  
  268.         self.stdout = stdout  
  269.         self.stderr = stderr  
  270.         self.base_env = environ  
  271.         self.wsgi_multithread = multithread  
  272.         self.wsgi_multiprocess = multiprocess  
  273.   
  274.     def run(self, application):  
  275.         """Invoke the application"""  
  276.         # Note to self: don't move the close()!  Asynchronous servers shouldn't  
  277.         # call close() from finish_response(), so if you close() anywhere but  
  278.         # the double-error branch here, you'll break asynchronous servers by  
  279.         # prematurely closing.  Async servers must return from 'run()' without  
  280.         # closing if there might still be output to iterate over.  
  281.         try:  
  282.             self.setup_environ()  
  283.             self.result = application(self.environ, self.start_response)  
  284.             self.finish_response()  
  285.         except:  
  286.             try:  
  287.                 self.handle_error()  
  288.             except:  
  289.                 # If we get an error handling an error, just give up already!  
  290.                 self.close()  
  291.                 raise   # ...and let the actual server figure it out.  
  292.   
  293.     def setup_environ(self):  
  294.         """Set up the environment for one request"""  
  295.   
  296.         env = self.environ = self.os_environ.copy()  
  297.         self.add_cgi_vars()  
  298.   
  299.         env['wsgi.input']        = self.get_stdin()  
  300.         env['wsgi.errors']       = self.get_stderr()  
  301.         env['wsgi.version']      = self.wsgi_version  
  302.         env['wsgi.run_once']     = self.wsgi_run_once  
  303.         env['wsgi.url_scheme']   = self.get_scheme()  
  304.         env['wsgi.multithread']  = self.wsgi_multithread  
  305.         env['wsgi.multiprocess'] = self.wsgi_multiprocess  
  306.   
  307.         if self.wsgi_file_wrapper is not None:  
  308.             env['wsgi.file_wrapper'] = self.wsgi_file_wrapper  
  309.   
  310.         if self.origin_server and self.server_software:  
  311.             env.setdefault('SERVER_SOFTWARE',self.server_software)  
  312.   
  313.     def finish_response(self):  
  314.         """ 
  315.         Send any iterable data, then close self and the iterable 
  316.  
  317.         Subclasses intended for use in asynchronous servers will want to 
  318.         redefine this method, such that it sets up callbacks in the event loop 
  319.         to iterate over the data, and to call 'self.close()' once the response 
  320.         is finished. 
  321.         """  
  322.         if not self.result_is_file() or not self.sendfile():  
  323.             for data in self.result:  
  324.                 self.write(data)  
  325.             self.finish_content()  
  326.         self.close()  
  327.   
  328.     def get_scheme(self):  
  329.         """Return the URL scheme being used"""  
  330.         return guess_scheme(self.environ)  
  331.   
  332.     def set_content_length(self):  
  333.         """Compute Content-Length or switch to chunked encoding if possible"""  
  334.         try:  
  335.             blocks = len(self.result)  
  336.         except (TypeError, AttributeError, NotImplementedError):  
  337.             pass  
  338.         else:  
  339.             if blocks==1:  
  340.                 self.headers['Content-Length'] = str(self.bytes_sent)  
  341.                 return  
  342.         # XXX Try for chunked encoding if origin server and client is 1.1  
  343.   
  344.     def cleanup_headers(self):  
  345.         """Make any necessary header changes or defaults 
  346.  
  347.         Subclasses can extend this to add other defaults. 
  348.         """  
  349.         if 'Content-Length' not in self.headers:  
  350.             self.set_content_length()  
  351.   
  352.     def start_response(self, status, headers,exc_info=None):  
  353.         """'start_response()' callable as specified by PEP 333"""  
  354.   
  355.         if exc_info:  
  356.             try:  
  357.                 if self.headers_sent:  
  358.                     # Re-raise original exception if headers sent  
  359.                     raise exc_info[0], exc_info[1], exc_info[2]  
  360.             finally:  
  361.                 exc_info = None        # avoid dangling circular ref  
  362.         elif self.headers is not None:  
  363.             raise AssertionError("Headers already set!")  
  364.   
  365.         assert isinstance(status, str),"Status must be a string"  
  366.         assert len(status)>=4,"Status must be at least 4 characters"  
  367.         assert int(status[:3]),"Status message must begin w/3-digit code"  
  368.         assert status[3]==" ""Status message must have a space after code"  
  369.         if __debug__:  
  370.             for name,val in headers:  
  371.                 assert isinstance(name, str),"Header names must be strings"  
  372.                 assert isinstance(val, str),"Header values must be strings"  
  373.                 assert not is_hop_by_hop(name),"Hop-by-hop headers not allowed"  
  374.         self.status = status  
  375.         self.headers = self.headers_class(headers)  
  376.         return self.write  
  377.   
  378.     def send_preamble(self):  
  379.         """Transmit version/status/date/server, via self._write()"""  
  380.         if self.origin_server:  
  381.             if self.client_is_modern():  
  382.                 self._write('HTTP/%s %s\r\n' % (self.http_version,self.status))  
  383.                 if 'Date' not in self.headers:  
  384.                     self._write(  
  385.                         'Date: %s\r\n' % http_date()  
  386.                     )  
  387.                 if self.server_software and 'Server' not in self.headers:  
  388.                     self._write('Server: %s\r\n' % self.server_software)  
  389.         else:  
  390.             self._write('Status: %s\r\n' % self.status)  
  391.   
  392.     def write(self, data):  
  393.         """'write()' callable as specified by PEP 333"""  
  394.   
  395.         assert isinstance(data, str), "write() argument must be string"  
  396.   
  397.         if not self.status:  
  398.             raise AssertionError("write() before start_response()")  
  399.   
  400.         elif not self.headers_sent:  
  401.             # Before the first output, send the stored headers  
  402.             self.bytes_sent = len(data)    # make sure we know content-length  
  403.             self.send_headers()  
  404.         else:  
  405.             self.bytes_sent += len(data)  
  406.   
  407.         # XXX check Content-Length and truncate if too many bytes written?  
  408.   
  409.         # If data is too large, socket will choke, so write chunks no larger  
  410.         # than 32MB at a time.  
  411.         length = len(data)  
  412.         if length > 33554432:  
  413.             offset = 0  
  414.             while offset < length:  
  415.                 chunk_size = min(33554432, length)  
  416.                 self._write(data[offset:offset+chunk_size])  
  417.                 self._flush()  
  418.                 offset += chunk_size  
  419.         else:  
  420.             self._write(data)  
  421.             self._flush()  
  422.   
  423.     def sendfile(self):  
  424.         """Platform-specific file transmission 
  425.  
  426.         Override this method in subclasses to support platform-specific 
  427.         file transmission.  It is only called if the application's 
  428.         return iterable ('self.result') is an instance of 
  429.         'self.wsgi_file_wrapper'. 
  430.  
  431.         This method should return a true value if it was able to actually 
  432.         transmit the wrapped file-like object using a platform-specific 
  433.         approach.  It should return a false value if normal iteration 
  434.         should be used instead.  An exception can be raised to indicate 
  435.         that transmission was attempted, but failed. 
  436.  
  437.         NOTE: this method should call 'self.send_headers()' if 
  438.         'self.headers_sent' is false and it is going to attempt direct 
  439.         transmission of the file1. 
  440.         """  
  441.         return False   # No platform-specific transmission by default  
  442.   
  443.     def finish_content(self):  
  444.         """Ensure headers and content have both been sent"""  
  445.         if not self.headers_sent:  
  446.             self.headers['Content-Length'] = "0"  
  447.             self.send_headers()  
  448.         else:  
  449.             pass # XXX check if content-length was too short?  
  450.   
  451.     def close(self):  
  452.         try:  
  453.             self.request_handler.log_request(self.status.split(' ',1)[0], self.bytes_sent)  
  454.         finally:  
  455.             try:  
  456.                 if hasattr(self.result,'close'):  
  457.                     self.result.close()  
  458.             finally:  
  459.                 self.result = self.headers = self.status = self.environ = None  
  460.                 self.bytes_sent = 0self.headers_sent = False  
  461.   
  462.     def send_headers(self):  
  463.         """Transmit headers to the client, via self._write()"""  
  464.         self.cleanup_headers()  
  465.         self.headers_sent = True  
  466.         if not self.origin_server or self.client_is_modern():  
  467.             self.send_preamble()  
  468.             self._write(str(self.headers))  
  469.   
  470.     def result_is_file(self):  
  471.         """True if 'self.result' is an instance of 'self.wsgi_file_wrapper'"""  
  472.         wrapper = self.wsgi_file_wrapper  
  473.         return wrapper is not None and isinstance(self.result,wrapper)  
  474.   
  475.     def client_is_modern(self):  
  476.         """True if client can accept status and headers"""  
  477.         return self.environ['SERVER_PROTOCOL'].upper() != 'HTTP/0.9'  
  478.   
  479.     def log_exception(self,exc_info):  
  480.         """Log the 'exc_info' tuple in the server log 
  481.  
  482.         Subclasses may override to retarget the output or change its format. 
  483.         """  
  484.         try:  
  485.             from traceback import print_exception  
  486.             stderr = self.get_stderr()  
  487.             print_exception(  
  488.                 exc_info[0], exc_info[1], exc_info[2],  
  489.                 self.traceback_limit, stderr  
  490.             )  
  491.             stderr.flush()  
  492.         finally:  
  493.             exc_info = None  
  494.   
  495.     def handle_error(self):  
  496.         """Log current error, and send error output to client if possible"""  
  497.         self.log_exception(sys.exc_info())  
  498.         if not self.headers_sent:  
  499.             self.result = self.error_output(self.environ, self.start_response)  
  500.             self.finish_response()  
  501.         # XXX else: attempt advanced recovery techniques for HTML or text?  
  502.   
  503.     def error_output(self, environ, start_response):  
  504.         import traceback  
  505.         start_response(self.error_status, self.error_headers[:], sys.exc_info())  
  506.         return ['\n'.join(traceback.format_exception(*sys.exc_info()))]  
  507.   
  508.     # Pure abstract methods; *must* be overridden in subclasses  
  509.   
  510.     def _write(self,data):  
  511.         self.stdout.write(data)  
  512.         self._write = self.stdout.write  
  513.   
  514.     def _flush(self):  
  515.         self.stdout.flush()  
  516.         self._flush = self.stdout.flush  
  517.   
  518.     def get_stdin(self):  
  519.         return self.stdin  
  520.   
  521.     def get_stderr(self):  
  522.         return self.stderr  
  523.   
  524.     def add_cgi_vars(self):  
  525.         self.environ.update(self.base_env)  
  526.   
  527. class WSGIServer(HTTPServer):  
  528.     """BaseHTTPServer that implements the Python WSGI protocol"""  
  529.     application = None  
  530.   
  531.     def __init__(self, *args, **kwargs):  
  532.         if kwargs.pop('ipv6'False):  
  533.             self.address_family = socket.AF_INET6  
  534.         HTTPServer.__init__(self, *args, **kwargs)  
  535.   
  536.     def server_bind(self):  
  537.         """Override server_bind to store the server name."""  
  538.         try:  
  539.             HTTPServer.server_bind(self)  
  540.         except Exception, e:  
  541.             raise WSGIServerException(e)  
  542.         self.setup_environ()  
  543.   
  544.     def setup_environ(self):  
  545.         # Set up base environment  
  546.         env = self.base_environ = {}  
  547.         env['SERVER_NAME'] = self.server_name  
  548.         env['GATEWAY_INTERFACE'] = 'CGI/1.1'  
  549.         env['SERVER_PORT'] = str(self.server_port)  
  550.         env['REMOTE_HOST']=''  
  551.         env['CONTENT_LENGTH']=''  
  552.         env['SCRIPT_NAME'] = ''  
  553.   
  554.     def get_app(self):  
  555.         return self.application  
  556.   
  557.     def set_app(self,application):  
  558.         self.application = application  
  559.   
  560. class WSGIRequestHandler(BaseHTTPRequestHandler):  
  561.     server_version = "WSGIServer/" + __version__  
  562.   
  563.     def __init__(self, *args, **kwargs):  
  564.         from django.conf import settings  
  565.         self.admin_media_prefix = settings.ADMIN_MEDIA_PREFIX  
  566.         # We set self.path to avoid crashes in log_message() on unsupported  
  567.         # requests (like "OPTIONS").  
  568.         self.path = ''  
  569.         self.style = color_style()  
  570.         BaseHTTPRequestHandler.__init__(self, *args, **kwargs)  
  571.   
  572.     def get_environ(self):  
  573.         env = self.server.base_environ.copy()  
  574.         env['SERVER_PROTOCOL'] = self.request_version  
  575.         env['REQUEST_METHOD'] = self.command  
  576.         if '?' in self.path:  
  577.             path,query = self.path.split('?',1)  
  578.         else:  
  579.             path,query = self.path,''  
  580.   
  581.         env['PATH_INFO'] = urllib.unquote(path)  
  582.         env['QUERY_STRING'] = query  
  583.         env['REMOTE_ADDR'] = self.client_address[0]  
  584.   
  585.         if self.headers.typeheader is None:  
  586.             env['CONTENT_TYPE'] = self.headers.type  
  587.         else:  
  588.             env['CONTENT_TYPE'] = self.headers.typeheader  
  589.   
  590.         length = self.headers.getheader('content-length')  
  591.         if length:  
  592.             env['CONTENT_LENGTH'] = length  
  593.   
  594.         for h in self.headers.headers:  
  595.             k,v = h.split(':',1)  
  596.             k=k.replace('-','_').upper(); v=v.strip()  
  597.             if k in env:  
  598.                 continue                    # skip content length, type,etc.  
  599.             if 'HTTP_'+k in env:  
  600.                 env['HTTP_'+k] += ','+v     # comma-separate multiple headers  
  601.             else:  
  602.                 env['HTTP_'+k] = v  
  603.         return env  
  604.   
  605.     def get_stderr(self):  
  606.         return sys.stderr  
  607.   
  608.     def handle(self):  
  609.         """Handle a single HTTP request"""  
  610.         self.raw_requestline = self.rfile.readline()  
  611.         if not self.parse_request(): # An error code has been sent, just exit  
  612.             return  
  613.         handler = ServerHandler(self.rfile, self.wfile, self.get_stderr(), self.get_environ())  
  614.         handler.request_handler = self      # backpointer for logging  
  615.         handler.run(self.server.get_app())  
  616.   
  617.     def log_message(self, format, *args):  
  618.         # Don't bother logging requests for admin images or the favicon.  
  619.         if self.path.startswith(self.admin_media_prefix) or self.path == '/favicon.ico':  
  620.             return  
  621.   
  622.         msg = "[%s] %s\n" % (self.log_date_time_string(), format % args)  
  623.   
  624.         # Utilize terminal colors, if available  
  625.         if args[1][0] == '2':  
  626.             # Put 2XX first, since it should be the common case  
  627.             msg = self.style.HTTP_SUCCESS(msg)  
  628.         elif args[1][0] == '1':  
  629.             msg = self.style.HTTP_INFO(msg)  
  630.         elif args[1] == '304':  
  631.             msg = self.style.HTTP_NOT_MODIFIED(msg)  
  632.         elif args[1][0] == '3':  
  633.             msg = self.style.HTTP_REDIRECT(msg)  
  634.         elif args[1] == '404':  
  635.             msg = self.style.HTTP_NOT_FOUND(msg)  
  636.         elif args[1][0] == '4':  
  637.             msg = self.style.HTTP_BAD_REQUEST(msg)  
  638.         else:  
  639.             # Any 5XX, or any other response  
  640.             msg = self.style.HTTP_SERVER_ERROR(msg)  
  641.   
  642.         sys.stderr.write(msg)  
  643.   
  644.   
  645. class AdminMediaHandler(handlers.StaticFilesHandler):  
  646.     """ 
  647.     WSGI middleware that intercepts calls to the admin media directory, as 
  648.     defined by the ADMIN_MEDIA_PREFIX setting, and serves those images. 
  649.     Use this ONLY LOCALLY, for development! This hasn't been tested for 
  650.     security and is not super efficient. 
  651.  
  652.     This is pending for deprecation since 1.3. 
  653.     """  
  654.     def get_base_dir(self):  
  655.         import django  
  656.         return os.path.join(django.__path__[0], 'contrib''admin''media')  
  657.   
  658.     def get_base_url(self):  
  659.         from django.conf import settings  
  660.         from django.core.exceptions import ImproperlyConfigured  
  661.         if not settings.ADMIN_MEDIA_PREFIX:  
  662.             raise ImproperlyConfigured(  
  663.                 "The ADMIN_MEDIA_PREFIX setting can't be empty "  
  664.                 "when using the AdminMediaHandler, e.g. with runserver.")  
  665.         return settings.ADMIN_MEDIA_PREFIX  
  666.   
  667.     def file_path(self, url):  
  668.         """ 
  669.         Returns the path to the media file on disk for the given URL. 
  670.  
  671.         The passed URL is assumed to begin with ``self.base_url``.  If the 
  672.         resulting file path is outside the media directory, then a ValueError 
  673.         is raised. 
  674.         """  
  675.         relative_url = url[len(self.base_url[2]):]  
  676.         relative_path = urllib.url2pathname(relative_url)  
  677.         return safe_join(self.base_dir, relative_path)  
  678.   
  679.     def serve(self, request):  
  680.         document_root, path = os.path.split(self.file_path(request.path))  
  681.         return static.serve(request, path, document_root=document_root)  
  682.   
  683.     def _should_handle(self, path):  
  684.         """ 
  685.         Checks if the path should be handled. Ignores the path if: 
  686.  
  687.         * the host is provided as part of the base_url 
  688.         * the request's path isn't under the base path 
  689.         """  
  690.         return path.startswith(self.base_url[2]) and not self.base_url[1]  
  691.   
  692. def run(addr, port, wsgi_handler, ipv6=False):  
  693.     server_address = (addr, port)  
  694.     httpd = WSGIServer(server_address, WSGIRequestHandler, ipv6=ipv6)  
  695.     httpd.set_app(wsgi_handler)  
  696.     httpd.serve_forever()  

 

 

参考资料

http://blog.csdn.net/lanphaday/article/details/6601123/

 

http://wiki.woodpecker.org.cn/moin/PythonCodingRule

 

http://www.elias.cn/Python/PythonStyleGuide

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics