⇤ ← Revision 1 as of 2008-06-29 01:03:44
Size: 509
Comment:
|
Size: 46763
Comment:
|
Deletions are marked like this. | Additions are marked like this. |
Line 9: | Line 9: |
= 文章大标 = ## 一句话本章精要精神 ''简述'' == 章标题1 == === 小节标题1 === {{{ #!python Python code }}} == 小结 == ## 总体语法等等叙述,注意给出相关知识的阅读指导 == 练习 == ## 设计实用练习,确保体例代码可以自由扩展出各种实用应用! |
'''CherryPy Essentials''' ''Translated By Feather.et.ELF ([email protected])'' 2008/09/06 [[TableOfContents]] = 目录 = 1. 前言 2. CherryPy 介绍 1. 概述 2. CherryPy 历史 3. 社区 4. CherryPy 项目规模 5. CherryPy 之外 6. 全书概览 7. 小结 3. 下载安装 CherryPy 1. 需求 2. 概述 3. 从 Tarball 安装 4. 通过 Easy Install 安装 5. 从 Subversion 安装 6. 测试安装 7. 随时更新 CherryPy 8. 小结 4. CherryPy 概览 1. 词汇表 2. 基本例子 3. 内建 HTTP 服务器 4. 内部引擎 5. 配置 6. 对象发布引擎 7. 库 * 自动导入特性 * 缓存模块 * Coverage 模块 * Encoding/Decoding 模块 * HTTP 模块 * Httpauth 模块 * Profiler 模块 * Sessions 模块 * Static 模块 * Tidy 模块 * Wsgiapp 模块 * XML-RPC 模块 8. 工具 9. 错误和异常处理 10. 小结 5. 深入了解 CherryPy 1. HTTP Compliance 2. 多重 HTTP 服务器 3. 多线程应用服务器 4. URI Dispaching * HTTP 方法 Dispacher * Routes Dispacher * 虚拟主机 Dispacher 5. Hook 到 CherryPy 核心引擎 6. CherryPy 工具箱 * 基本认证工具 * 缓存工具 * 解码工具 * 摘要认证工具 * 编码工具 * 错误重定向工具 * Etag 工具 * Gzip 工具 * 忽略 Headers 工具 * 记录 Headers 工具 * 记录跟踪返回工具 * 代理工具 * 参考工具 * 请求头工具 * Trailing Slash 工具 * XML-RPC 工具 * 工具箱 * 创建新的工具 7. 静态资源服务 * 使用 Staticfile 工具提供单个文件 * 使用 Staticdir 工具提供整个目录 * 绕开 Static 工具提供静态内容 8. WSGI 支持 * 使用 CherryPy 支持 WSGI 应用 * 使用第三方服务器支持 CherryPy WSGI 应用 9. 小结 6. 一个图片 Blog 应用 1. 图片 Blog 应用 2. 图片 Blog 实体 3. 词汇表 4. DBMS 概览 * 关系数据库管理系统(RDBMS) * 面向对象数据库管理系统(OODBMS) * XML 数据库管理系统(XMLDBMS) 5. 对象关系映射 * Python 对象关系映射 6. 图片 Blog 实体模型的建立 * 映射实体 * 单位和单位属性 * 联合单位 * 沙箱接口 7. 查询单位 8. 扩展数据访问层 9. 小结 7. Web 服务 1. 传统 Web 开发 * 分工 2. REST 3. 统一资源定位符 4. HTTP 方法 5. 把它们放在一起 6. CherryPy 的 REST 接口 7. Atom 发布协议 8. Atom XML 文档格式 9. APP 实现 10. 小结 8. 表现层 1. HTML 2. XML 3. XHTML 4. CSS 5. DHTML 6. 模板 7. Kid 模板引擎 * 概览 * Kid 属性 * 基于 XML 的模板语言 * 变量替换 * 条件语句 * 循环机制 * 扩展性 * 其他属性 8. 图片 Blog 设计表现 * 面向用户代理 * 工具 * 全局设计目标 * 设计目录布局 * CherryPy - 封装模板渲染过程 9. 图片 Blog 设计细节 * 基本结构 10. Mochikit 11. 开发图片 Blog 设计 * HTML 代码 * 添加链接 * 处理最终用户行为 * 修改模板 * 修改 CSS * 可扩展性 12. 小结 9. Ajax 1. 富客户端应用的流行 2. Ajax * Ajax - 优点和缺点 * 在后台: XMLHttpRequest * 执行 GET 请求 * 执行内容协商 GET 请求 * 执行 POST 请求 * 执行 PUT, HEAD, 或 DELETE 请求 * Cookies * 使用摘要或基本方案进行认证 3. JSON 4. 在我们的应用里使用 Ajax * 定义所需名称空间 * 实现名称空间 * 给类添加方法 * 创建新相册的方法 * 更新已有相册的方法 * 删除已有相册的方法 5. 小结 10. 测试 1. 为什么要测试 2. 计划测试 3. 常见测试方法 4. 单元测试 * unittest * doctest 5. 单元测试 Web 应用 6. 功能测试 * 测试下的应用程序 * Selenium Core * Selenium IDE * Selenium Remote Control 7. 小结 11. 部署应用 1. 配置 * CherryPy - Web 及引擎配置系统 * 图片 Blog 应用配置系统 2. 部署 * Apache 下的 mod_rewrite 模块 * Lighttpd 下的 mod_proxy 模块 * Apache 下的 mod_python 模块 * mod_python 与 WSGI 应用 3. SSL * 创建认证和私钥 * 使用 CherryPy 的 SSL 支持 * 使用 Lighttpd 的 SSL 支持 * 使用 Apache 的 mod_ssl 支持 4. 小结 12. 索引 -------- = 前言 = 在过去几年里, 我们经历了 Internet 大爆发, 许多编程语言都开始提供大量的 web 开发工具, 库, 以及框架. Python 编程语言同样提供了这些环境, 例如 Zope 和 Twisted , 它们中的很多 只是很小的社区. 这时, CherryPy 出现了, 它的作者... == 本书包含内容 == * 第一章 CherryPy 背后的故事以及对其的概览 * 第二章 == 你需要准备的东西 == 本书假定你已经安装好如下程序包. * Python 2.4 或更高版本 * CherryPy 3.0 == 本书的读者 == 本书原则上面向 web 开发人员. ... == 格式约定 == 本书中, 你会发现很多文本样式, 分别用于提供不同的信息. 这里做一简要说明. 代码有三种样式. 文本中的代码单词例如: A newer and more common way of deploying a package is to use the {{{easy_install}}} command to install eggs. 代码块例如: {{{ blody { background-color: #663; clor: #fff; } p { text-align: center; } }}} 命令行输入使用如下格式: {{{ python ez_setup.py }}} 新 '''术语''' 和 '''重要单词''' 使用黑体字显示. 屏幕上的字, 例如在菜单或是 对话框中, 使用: 下一步, 单击 '''All''' 按钮, 运行这些测试. {{{ WARN 警告和重要评注将出现在这里. }}} {{{ TIP 各种 Tips 和小技巧将出现在这里. }}} == 读者反馈 == 我们欢迎来自读者的反馈信息. 请让我们知道你对这本书的看法, 无论喜欢与否. 读者反馈信息对我们的进步来说是很重要的, 同时也可以为大家提供更好的服务. 一般反馈信息请发送邮件到 [[email protected]] , 记得在邮件里包含本书 的名字. 如果你需要我们出版某类图书, 请在 [http://www.packtpub.com www.packtpub.com] 站点上的 '''SUGGEST A TITLE''' 给我们发送短信, 或是发送邮件到 [[email protected]] . 如果你想就某一擅长的话题写作书籍或是为图书贡献的话, 请参考 [http://www.packtpub.com/authors www.packtpub.com/authors] 上的作者指导. == 客户支持 == 现在, 你是一本 Packt 图书的拥有者, 这里有些相关信息帮助你利用好这本书. == 下载本书的范例代码 == 请访问 [http://www.packtpub.com/support] , 从列表中选择本书的名字, 下载 本书的范例代码以及其他资料. 所下载文件包含使用说明. == 勘误 == 尽管我们尽力确保本书内容的正确性, 但是错误是难免的. 如果你在我们的书里 发现错误 - 正文或是代码 - 请报告给我们, 不胜感谢. 这样会帮助其他读者使 他们不会陷入疑惑中, 同时也有助于我们改进后继版本. 如果你发现任何错误, 请汇报至 [http://www.packtpub.com/support] , 选择书名, 点击 '''Submit Errata''' 链接, 输入你所发现错误的细节内容. 一旦确认, 你所提交 的勘误将被加入到现有勘误列表中. (访问该站点你可以获得现有勘误列表). == 问题 == 如果你有关于本书的某方面的问题, 你可以联系 [[email protected]] , 我 们会尽力回答你的信件. -------- = 1. 介绍 CherryPy = 万维网 (World Wide Web) 的使用呈指数级增长, 已经成为我们生活中的一个重 要组成. 从开发者的观点来看, Web 提供了大量的机会和乐趣. 然而, 面向 Web 的技术太过繁杂, 很难决定使用哪个. 本书的目标是展示其中的一种, CherryPy, 一个 Python web 应用库. 本章将介绍 CherryPy 的特性和优点, 首先概括 CherryPy 的历史, 然后介绍它 成功的最大原因 - 友好的社区. 最后回顾下 CherryPy 革命的关键原则. == 1.1. 概述 == CherryPy 为 Python 开发者提供了一个 HTTP 协议的友好接口. HTTP 是 World Wide Web 的支柱. Web 应用在过去几年里快速增长. 随着爆发而来的是各种编 程语言中大量的工具箱, 库, 以及框架. 所有的这些都是为了减少 web 开发人 员的劳动. 在这样的背景下, CherryPy 出现了, 它建立在 Python 的动态特性 上, 将 HTTP 协议绑定到一个符合 Python 习惯的 API 上. Python 社区开发了大量的 web 库和框架, 以至于出现了个玩笑 "as much as a worry". 尽管其中只有少数吸引了社区的注意 (TurboGears, Django, Zope 等), 但是不可否认的是, 每个库或是框架都有对 Python 与 HTTP 接口的独特 处理思想, 或多或少都有一定的影响. CherryPy 的出现是因为那时候的 Remi Delon , 它的作者, 在已有选择里没有找到他想要的那个. 有很开发者被 CherryPy 的优点吸引, 加入社区为 CherryPy 的设计贡献力量. 今天, 这个项 目有者强大的社区基础, 在不同的环境下将它作为日常使用. == 1.2. CherryPy 历史 == Remi Delon 在 2002 年六月发布了 CherryPy 的第一个版本. 这是一个成功的 Python web 库的起始点. Remi 是一个法国黑客, 他相信 Python 是 web 应用 开发的最终选择. Remi 的方法吸引了很多开发者. * CherryPy 类是一个 Python 扩展, 它将数据和表现分离开来. 这与 MVC (model-view-controller, 模型-视图-控制器) 模式很类似. * CherryPy 类必须被 CherryPy 引擎处理编译, 生成一个自包含的 Python 模块, 其中嵌入了整个应用以及它自己内建的 web 服务器. CherryPy 会把 URL 连同它的查询字符串映射到一个 Python 方法调用, 例如 [http://somehost.net/echo?message=hello] 会被映射到 {{{echo(message='hello')}}} . 在随后的两年里, 这个项目被社区支持, Remi 发布了一些改进版本. 在 2004 年六月, 开始了一场关于项目未来发展以及是否维持结构不变的讨论. 最主要问题之一是编译阶段, 这让 Python 开发者感觉不自然. 集体讨论和关于 一些项目原则的讨论最终发展成为对象发布引擎和过滤器的概念, 这很快成为 CherryPy 2 的一个核心组成. 最后, 在 2004 年十月, CherryPy 2 alpha 的第一个版本发布了, 作为这些核 心概念的证明. 随后六个月, 为了发布一个稳定版本(2005 年四月), 大家都投 入到的紧张工作里. CherryPy 2.0 是一个很大的成功; 不过, 大家也认识到它 的设计仍然可以改进, 它需要重构. 在进一步的社区反馈/讨论后, CherryPy 的 API 做了一些修改, 以使它的结构 更完美, 这便是随后在 2005 年十月发布的 CherryPy 2.1.0 . 该版本被流行的 TruboGears 项目采用 - 它是由一堆项目组成的一个 web mega-框架. 开发团队 在 2006 年四月发布了 CherryPy 2.2.0 . CherryPy 作为核心组成出现在被广泛采用的 TruboGears 框架上意味着 CherryPy 的一些方面出现了越来越多问题. 例如它的 WSGI 支持, 缺乏最新的 文档, 还有它不是很出众的性能. 在重要的实际需求中, 不破坏向后兼容性去 扩展 CherryPy 2 是很困难的. 最终, 大家决定向 CherryPy 3 前进. CherryPy 于 2006 年十二月被发布. == 1.3. 社区 == 在过去几年里, 如果没有社区, CherryPy 就不会有现在这样的成功. Remi 清楚 地知道他并不想让 CherryPy 只是作为自己的私人项目存在, 而是成为一个社区 性质的项目. CherryPy 一直都有一部分追随者, 但事实上 CherryPy 社区是在版本 2.0 开始 的. 在 2004 年十一月, '''Open and Free Technology Community''' ('''OFTC''') 网络上注册了一个 IRC 频道, 开发者和用户可以通过它快速交换想 法, 或是报告错误. 这个频道慢慢吸引了越来越多的长期用户, 被广泛认为是一 个友好的地方. 除 IRC 频道外, 针对开发者和用户的邮件列表也被创建. 还有 一个 CherryPy 用户的 blog feed 聚合站点也建立了起来, 你可以访问 [http://planet.cherrypy.org] . == 1.4. CherryPy 项目优点 == * 简单: 使 CherryPy 尽可能简单是主要目标之一. 这可以避免库 over engineering 项目. 由于库本身的范围比较窄, 开发者可以把注意力集中在 API 和社区反馈信息上. * 自包含(self-contained): 从一开始, Remi 就决定 CherryPy 的核心不会需要任何第三方 Python 库, CherryPy 完全依赖于 Python 标准库. * 非强制性: 确保 CherryPy 和其用户的方式一致, 提供的工具没有假定开发者的特定使用方式. * 开放式讨论: 开发团队会听取社区的反馈. 这并不意味着每个要求都被采纳, 不过大多数都会被仔细讨论评价. * 有趣: 开发开源项目时, 贡献者不会觉得这只是他们的日常工作; 事实上他们会以此为乐. 同样地, 对于 CherryPy 用户来说, 趣味同样是一个重要部分, 发现这一点会让我们都成为更好更有创造力的开发人员. == 1.5. 超越 CherryPy == 在一开始, CherryPy 吸引了一小群用户, 但它的设计导致它不能被更多人接受 或是更广泛使用. 此外, 在那时, Python web 开发领域几乎完全被 Zope 平台 占据. CherryPy 2 发布时, 它的概念被社区热烈欢迎, 吸引了更多的用户在应 用中使用 CherryPy 或是基于它建立他们自己的包. 在 2005 年五月, Kevin Dangoor 发布了 TruboGears - 使用一堆开源产品建立 的一个 web 开发框架. 在框架里 Kevin 选择了 CherryPy 处理 HTTP 层, SQLObject 将对象映射到数据库, Kid 用做 XHTML 模板, MochiKit 作为客户端 处理. 这只是在另个 Python web 框架, Django 向社区开放的几个月之后. 这 两个项目都很快在 Python 社区流行起来, 由于它们之间的小小竞争, 使得流行 趋势更加迅速. TurboGears 的急速发展也推进了 CherryPy 知名度, 吸引了大 量的新用户. 这些新开发者为 CherryPy 增加了许多新特性, 同时也修复了一些缺点, 最终作 为 CherryPy 3 发布, 至本书完成时候库的最稳定版本. CherryPy 的未来是明确的; Robert Brewer 所做的杰出工作使得库的处理速度 有了很大提高. TurboGears 的未来版本将会迁移到 CherryPy 3 上, 这会为开 发团队带来新的需要解决的问题, 也会让 CherryPy 继续前进. == 1.6. 全书概览 == 本书旨在介绍 CherryPy , 使你有信心在你自己的 web 应用中把它用好. 另外 我们也将展开对 web 应用设计的讨论. 首先本书将介绍 Python 社区使用的一 些获得安装 CherryPy 的常见方法, 例如使用 setup tools 和 easy_install. 同时你将大致了解 CherryPy 的主要概念, 逐渐明白这个库可以 做什么. 然后我们深入到库的一些特性, 例如 HTTP 能力, URI 调度器, 如何扩 展库, 以及它的 WSGI 支持. 此时, 你会对 CherryPy , 它的设计, 以及如何使用它有深入的了解. 然后, 本 书将通过一个简单的 blog 应用介绍各种技术和工具, 例如对象关系映射, web 服务, 以及 Ajax , 使你对 web 开发的各个层有一定的的了解. 我们将提出 blog 应用的目标和边界(x), 回顾 Python 数据库处理的情况, 解 释对象关系映像. 这里我们会展示 Python ORM 之一, Dejavu. 本书还会讨论 REST 和 Atom 发行协议(Atom Publishing Protocol), 它们提供了扩可展 web 应用的设计方法. 然后我们介绍 blog 应用的表现层, 它使用了 Kid 模板引擎 和 MockiKit JavaScript 库. 随后本书简单讨论了 Ajax 以及如何使你的应用 受益于它. 我们还会看到你的应用如何才能调用 web 服务. 然后, 本书会介绍 web 应用测试领域. 这来自于单元测试, 它通过其中的函数测试概念载入测试. 本书最后会介绍一些部署 web 应用到独立应用或是常见 web 服务器(例如 Apache, lighttpd)的方法. 虽然其中一些扩展章节并没有讨论 CherryPy 本身, 但是所有章节都以介绍 web 应用开发为核心. 本书教你使用 CherryPy , 你将有基础和动力继续学习更多相关话题. == 1.7. 小结 == 在读完这些介绍后, 你应该对本书一些了解. CherryPy 是一个简单且强大的 Python 库, 如果你寻找一个把 HTTP 协议中艰涩的部分封装在底层却同时不失 其优点的库, 那么 CherryPy 是你的正确选择. CherryPy 社区在过去的时间里 努力使这样一个产品成为可能; 本书会给你正确的指导, 让你利用好它. = 2. 下载安装 CherryPy = 和大多开源项目类似, CherryPy 可以通过多种方法下载安装. 这里我们将介绍 下面三种方法: * 使用 tarball * 使用 easy_install * 从 Subversion 获得最新版本源代码 每种方法都会为这个项目的用户带来不同的价值, 了解这点很重要. 当你读到这一章时, 你应该可以取得并部署 CherryPy , 同时知道如何在你自己 的软件里使用这些技术. == 2.1. 需求 == 本书通篇都假设你安装好了这些程序包: * Python 2.4 或更高版本 * CherryPy 3.0 同时我们假定你已经对 Python 本身有足够的了解, 本书将不会涵盖语言本身的 内容. == 2.2. 概述 == 安装 Python 模块或包通常只是很简单的过程. 首先我们讨论最常见的建立和安 装新的包的方法, 多亏有了 Python 2.0 中被加入的一个标准模块 distutils . 该模块提供了一个描述包结构的简洁接口, 包的需求关系, 编译规则都在其中. 对于用户来说, 通常只意味着输入命令: {{{ python setup.py build python setup.py install }}} 第一条命令使用开发者指定的规则编译包, 在依存关系不满足的时候把错误汇报 给最终用户. 第二条命令将包安装到 Python 的默认第三方包/模块保存目录. 注意第二条命令会调用第一条命令来做快速检查, 确保在最后一次运行后没有任 何改动发生. 包和模块的默认位置在: * 在 UNIX 或 Linux 下 {{{/usr/local/lib/python2.4/site-packages}}} 或 {{{/usr/lib/python2.4/site-packages}}} * 在 Microsoft Windows 下 {{{C:\Python\lib\site-packages}}} 或 {{{C:\Python2X\lib\site-packages}}} * 在 MacOS 下 {{{Python:Lib:site-packages}}} 在 UNIX 或 Linux 下它可能因为你的 Python 安装位置不同而不同, 但是这些 目录路径几乎都是相似的. 导入模块时, Python 会在一系列目录中查找模块, 其中一些是默认的, 另一些则由用户提供, 直到它发现一个匹配的模块, 否则会 引发一个异常. 修改搜索列表可以通过定义 {{{PYTHONPATH}}} 环境变量或是直 接在代码中进行修改: {{{ import sys sys.path.append(path) }}} {{{ NOTE PYTHONPATH 环境变量是 Python 引擎启动时读取的环境变量之一. 它包含需要加入搜索列表的地三方模块和包的路径. }}} 另个方法是创建一个和包同名的文件, 以 {{{.pth}}} 作为扩展名. 该文件内写入 包的完整路径. 虽然这很简洁, 但这个方法有它的局限性. 由于 {{{sys.path}}} 列表是有序的, 如果两个路径包含相同模块的不同版本, 那么你必须保证你的应用中导入的那个 可以被首先访问到. 这导致我们陷入包的版本问题. 假设你在全局安装的 Python 里安装了 CherryPy 2.2.1 , 它会被安装到 {{{/usr/local/lib/site-packages/cherrypy}}} 中. 然而, 路径并未包含任何版 本信息, 如果你必须安装 CherryPy 3.0.0 , 那么你必须覆盖已有安装的版本. 幸运的是, Python 社区提供了一个解决方法 - eggs . '''egg''' 是一个压缩的 文件夹, 连同包内的所有文件和子目录. 它的文件名含有包的版本细节. {{{ NOTE egg 是可分发的压缩包, 它含有 Python 包或模块的相关信息(例如作者, 版本等). }}} 例如, Python 2.4 编译 CherryPy 2.2.1 会得到文件 {{{Cherrypy-2.2.1-py2.4.egg}}} . egg 自身并不是很有用; 它的部署需要 easy_install , 一个包含处理 egg 逻辑的 Python 模块. 这意味着你可以在同 个目录下部署不同版本, 让 easy_install 去决定导入哪个版本. 下一章节我们将看看在通常情况下如何安装 CherryPy . == 2.3. 从 Tarball 安装 == '''Tarball''' 是压缩的归档文件. 文件名来自于 UNIX 及关联系统的 tar 工具. {{{ NOTE 最广泛使用的是 gzip 工具, tarball 的扩展名为 .tar.gz 或是 .tgz. }}} CherryPy 为每个发布都提供了一个 tarball, 无论是 alpha, beta, release condidate, 或是稳定版本. 你都可以从 [http://download.cherrypy.org/] 获得. CherryPy tarball 包含库的完整源代码. 从 tarball 安装 CherryPy 需要如下几步: 1. 从 [http://download.cherrypy.org/] 下载你感兴趣的版本 2. 找到文件存放位置, 解压缩之: * 在 Linux 下, 使用如下命令: BR {{{ tar zxvf cherrypy-x.y.z.tgz }}} 其中 x.y.z 是你所获得的版本号. * 如果你在 Microsoft Windows 下, 你可以使用 7-zip 等解压缩工具在图形 界面下解压文件. 3. 移动到解压出的文件夹, 输入如下命令编译 CherryPy BR {{{ python setup.py build }}} 4. 最后, 使用如下命令创建全局安装(你可能需要管理员权限): {{{ python setup.py install }}} {{{ NOTE 这些命令必须使用命令行输入. 在 Microsoft Windows 下你需要执行 DOS 命令行. }}} 以上几步会为创建系统默认 Python 环境的全局安装. 有时候你可能并不想这样 或者不能这样. 例如你可能不只为一个版本的 Python 安装 CherryPy; 这时候 你需要在第 3 步或第 4 步指定正确的 Python 文件. 或者你可能不想在全局环境里安装, 如果在 UNIX 下你只需要把第 4 步替换为: {{{ python setup.py install --home=~ }}} 这会将文件放置在 {{{$HOME/lib/python}}}, 其中 {{{$HOME}}} 是你的主目录. 在 Microsoft Windows, 下不存在 {{{HOME}}}, 你应该使用如下命令: {{{ python setup.py install --prefix=c:\some\path }}} 路径自身并不重要, 你可以使用符合你的环境的任意路径. 然后你必须确认 Python 在你需要导入模块的时候检查该路径. 最简单的方法是 设置 {{{PYTHONPATH}}} 环境变量: * 在 Linux 下使用 bash shell 执行: BR {{{ export PYTHONPATH=~/lib/python }}} * 在 Microsoft Windows 下使用命令行输入: BR {{{ set PYTHONPATH=/some/path }}} {{{ TIP 注意这只会对你当前打开的命令行窗口有效, 在关闭窗口后会被丢弃. 若想永久改变环境变量, 请通过 系统属性 | 高级 | 环境变量 设置. }}} * 在 MacOS 下使用 csh shell 输入: BR {{{ setenv PYTHONPATH "/some/path" }}} {{{PYTHONPATH}}} 环境变量会在 Python 解释器启动的时候被读取, 自动加入它 的内部系统路径({{{sys.path}}}). == 2.4. 通过 Easy Install 安装 == {{{easy_install}}} 模块提供了增强的 Python 包和模块部署能力, 你可以在 '''Python Enterprise Application Kit'''('''PEAK''') 网站得到它. 从开发者的观点来看, 它提供了一个很简单的模块导入 API, 你可以轻松地导入 指定版本或是指定某一版本范围. 例如, 你可以通过如下代码导入在环境中可找 到的第一个版本号大于 2.2 的 CherryPy : {{{ >>> from pkg_resources import require >>> require("cherrypy>=2.2") [Cherrypy 2.2.1 (/home/sylvain/lib/python/ Cherrypy-2.2.1-py2.4.egg)] }}} 从用户的观点看, 它简化了下载, 编译, 部署 Python 的过程. 在安装 CherryPy 之前, 我们必须安装 easy_install 本身. 从 [http://peak.telecommunity.com/dist/ez_setup.py] 下载 {{{ez_setup.py}}} 模 块, 然后使用有管理权限的用户执行: {{{ python ez_setup.py }}} 如果你没有管理员权限, 你可以使用 {{{-install-dir}}} ({{{-d}}}) 选项: {{{ python ez_setup.py -install-dir=/some/path }}} 确保 {{{/some/path}}} 在 Python 的系统路径中. 你可以把 {{{PYTHONPATH}}} 设 置为该目录. 这样之后, 你的环境就支持 easy_install 了. 然后安装支持 easy_install 的 Python 产品, 你只需要如下命令: {{{ easy_install product_name }}} easy_install 会在 '''Python Package Index'''('''PyPI''') 搜索指定产品. PyPI 是一个 Python 产品信息的主要仓库. 为了部署 CherryPy 的最新可用版本, 你需要执行以下命令: {{{ easy_install cherrypy }}} easy_install 会下载 CherryPy , 编译, 然后安装到全局 Python 环境. 如果 你需要把它安装到指定位置, 请输入如下命令: {{{ easy_install --install-dir=~ cherrypy }}} 安装后, 你会在指定目录得到一个名称为 {{{cherrypy.x.y.z-py2.x.egg}}} 的文 件, 具体文件名取决于当前 CherryPy 的最新版本号. == 2.5. 从 Subversion 安装 == Subversion 是一个很优秀的开源版本控制系统, 开发者可以通过它安全有效地 控制项目. 这样的系统的基本原则是注册一个资源, 然后记录它的每次更改, 这样开发者就 可以恢复到任意一个先前版本, 比较两个版本, etc. 资源可以是源代码文件, 二进制文件, 图像, 文档, 或是其他机器可读的格式. Subversion 是自动的, 也就是说如果一个 commit 失败, 那么整个 commit 就 算是失败的. 另一方面说, 如果它成功的话, 整个项目的版本号会增加, 而不是 只涉及这个文件. {{{ NOTE Subversion 一般被看做 CVS 的继承者, 它更友好. 当然, 还存在着其他版本管理系统, 例如 Monotone 或 Darcs. }}} 在 Linux, 你可以从源代码安装 Subversion 或是使用软件包管理器. 我们来看 看如果从源代码安装的过程. 1. 从 [http://subversion.tigris.org/] 获得最新的 tarball 2. 然后在命令行里输入命令: BR {{{ tar zxvf subversion-x.y.z.tar.gz }}} 3. 在解压后得到的目录地输入: {{{./configuure}}} 4. 使用 {{{make}}} 命令编译包. 5. 你可能会用到 Python 的 Subversion 绑定: BR {{{ make swig-py }}} 6. 全局安装 Subversion , 你需要在管理员权限下执行: BR {{{ make install; make install-swig-py }}} 大多情况下, 在 Linux 或是 UNIX 系统中, 通过命令行使用 Subversion 是很 简单的. 当然, 如果你喜欢图形界面的话, 我推荐你安装客户程序, 例如 eSvn 或是 kdesvn. 在 Microsoft Windows 下, 直接使用图形应用程序更简单, 例如 TortoiseSVN, 当然, 它也会安装 Subversion 客户端. 在以下情况推荐使用 Subversion 安装 CherryPy : * 某一特性或是 bug 修复只在开发中代码存在 * 你决定研究 CherryPy 本身 * 你需要从主干上分出一个分支来实现一个新特性, 新设计, 或是在先前版本中 修复 bug 为了使用该项目的最新版本, 你需要检查仓库的 trunk 目录. 在 shell 下输入 下面的命令: {{{ svn co http://svn.cherrypy.org/trunk cherrypy }}} {{{ TIP 在 Microsoft Windows 下, 你可以通过命令行或是直接通过 TortoiseSVN 的图形界面取得代码. 具体请参阅它的文档. }}} 这将创建一个 {{{cherrypy}}} 目录, 然后将完整源代码下载到里面. 通常并不推 荐将开发中的版本部署到全局环境里; 你需要使用如下命令将 CherryPy 安装到 一个本地目录: * 在 Linux 及相关系统下, 使用命令行输入: BR {{{ python setup.py install --home=~ }}} * 在 Microsoft Windows 下使用命令行输入: BR {{{ python setup.py install --prefix=c:\some\path }}} 然后, 记得设置好你的 {{{PYTHONPATH}}} 环境变量. 注意, 目录并不重要, 只要它能被 Pyhon 处理, 无论是使用环境变量还是通过 标准的 sys 模块, 都是可以的. == 2.6. 测试你的安装 == 无论你打算用什么方法把 CherryPy 安装部署到你的环境里, 你必须能够从 Python 命令行导入它: {{{ >>> import cherrypy >>> cherrypy.__version__ '3.0.0' }}} 如果你没有把 CherryPy 安装到全局环境下, 别忘记设置环境变量, 否则你将得 到如下的错误信息: {{{ >>> import cherrypy Traceback (most recent call last): File "<stdin>", line 1, in ? ImportError: No module named cherrypy }}} == 2.7. 更新CherryPy == 更新或是升级 CherryPy 的方法取决于你是如何安装它的. * 使用 tarball 安装 BR 确保升级成功的最干净的方法就是从含有包的目录里删除整个包, 然后使用前 边介绍的方法安装库 * 使用 easy_install 安装 BR 升级是 easy_install 提供的重要特性之一. BR {{{ easy_install -U cherrypy }}} 由于 egg 包含库和其版本信息, 你完全可以忽视已存在的 egg 去安装新的 egg. 不过请确保应用在执行的时候导入的是它需要的包. * 使用 Subversion 安装 这种方法的一个好处是你几乎可以连续地升级整个库. 你需要在保存源代码的 目录输入 {{{svn update}}} 命令, 然后执行 {{{python setup.py install}}} 覆盖已有安装. {{{ NOTE 在升级前记得备份你的文件, 这几乎总是正确的. }}} == 2.8. 小结 == 我们在本章讨论了安装 CherryPy 的三种方法. 传统方法是使用包含 Python 包 的所有文件的归档安装, 执行归档文件里提供的 {{{setup.py}}} 文件. 更常见的 方法是使用 {{{easy_install}}} 安装 egg. 最后, 如果你想和 CherryPy 的最新 开发保持同步的话, 你可以从它的 Subversion 仓库获得包. 无论使用哪种方法, 你都可以得到一份可用的 CherryPy. = 3. CherryPy 概览 = 在第一章我们简单介绍了 CherryPy 的一些概念;现在我们深入了解下该项目 是如何设计和构造的. 首先我们将讨论一个简单的 CherryPy 例子. 然后讨论 CherryPy 核心, 对象发布引擎, 以及它是如何将 HTTP 协议封装到一个面向对 象的库里的. 下一步我们将核心钩子(hook), CherryPy 库, 以及工具机制. 然后我们会回顾 CherryPy 的错误和异常处理, 以及如何使用它. 学完本章后, 你将对 CherryPy 库有一个很好的了解; 在以后的学习中你可能需 要再回顾本章的内容. == 3.1. 词汇表 == 为了避免误解, 我们首先定义一些在本书会出现的关键词. web 服务器:: :: web 服务器是处理 HTTP 协议的接口. 它将传入的 HTTP 请求转换为传入应用服 :: 务器的实体, 同时将来自应用服务器的信息转换并返回到 HTTP 应答中. 应用:: :: 应用是接受信息单位的软件, 应用各种逻辑后, 返回处理后的信息单位. 应用服务器:: :: 应用服务器是为一个或多个应用提供服务平台的部件. web 应用服务器:: :: web 应用服务器是 web 服务器和应用服务器的集合体. CherryPy 是一个 web 应用服务器. == 3.2. 基本例子 == 为了展示 CherryPy 库, 我们将讨论一个很基本的 web 应用, 用户可以通过 HTML 表单在主页面上发表评论. 评论按照创建时间倒序排列. 我们将使用 session 对象来保存评论作者的名字. 每条评论都有一个 URI, 类似 {{{/note/id}}} 的格式. 创建一个名为 {{{note.py}}} 的空文件, 写入如下代码. {{{ #!/usr/bin/python # -*- coding: utf-8 -* # Python standard library imports import os.path import time ############################################################### #The unique module to be imported to use cherrypy ############################################################### import cherrypy # CherryPy needs an absolute path when dealing with static data _curdir = os.path.join(os.getcwd(), os.path.dirname(__file__)) # We will keep our notes into a global list _notes = [] ############################################################### # A few HTML templates ############################################################### _header = """ <html> <head> <title>Random notes</title> <link rel="stylesheet" type="text/css" href="/style.css"></link> </head> <body> <div class="container">""" _footer = """ </div> </body> </html>""" _note_form = """ <div class="form"> <form method="post" action="post" class="form"> <input type="text" value="Your note here..." name="text" size="40"></input> <input type="submit" value="Add"></input> </form> </div>""" _author_form = """ <div class="form"> <form method="post" action="set"> <input type="text" name="name"></input> <input type="submit" value="Switch"></input> </form> </div>""" _note_view = """ <br /> <div> %s <div class="info">%s - %s <a href="/note/%d">(%d)</a></div> </div>""" ############################################################### # Our only domain object (sometimes referred as to a Model) ############################################################### class Note(object): def __init__(self, author, note): self.id = None self.author = author self.note = note self.timestamp = time.gmtime(time.time()) def __str__(self): return self.note ############################################################### # The main entry point of the Note application ############################################################### class NoteApp: """ The base application which will be hoested by CherryPy """ # Here we tell CherryPy we will enable the session # from this level of the tree of published objects # as well as its sub-levels _cp_config = { 'tools.sessions.on': True } def _render_note(self, note): """Helper to render a note into HTML""" return _note_view % (note, note.author, time.strftime("%a, %d %b %Y %H:%M:%S", note.timestamp), note.id, note.id) @cherrypy.expose def index(self): # Retrieve the author stored in the current session # None if not defined author = cherrypy.session.get('author', None) page = [_header] if author: page.append(""" <div><span>Hello %s, please leave us a note. <a href="author">Switch identity</a>.</span></div>""" % (author,)) page.append(_note_form) else: page.append("""<div><a href="author">Set your identity</a></span></div>""") notes = _notes[:] notes.reverse() for note in notes: page.append(self._render_note(note)) page.append(_footer) # Returns to the CherryPy server the page to render return page @cherrypy.expose def note(self, id): # Retrieve the note attached to the given id try: note = _notes[int(id)] except: # If the ID was not valid, let's tell the # client we did not find it raise cherrypy.NotFound return [_header, self._render_note(note), _footer] @cherrypy.expose def post(self, text): author = cherrypy.session.get('author', None) # Here if the author was not in the session # we redirect the client to the author form if not author: raise cherrypy.HTTPRedirect('/author') note = Note(author, text) _notes.append(note) note.id = _notes.index(note) raise cherrypy.HTTPRedirect('/') class Author(object): @cherrypy.expose def index(self): return [_header, _author_form, _footer] @cherrypy.expose def set(self, name): cherrypy.session['author'] = name return [_header, """ Hi %s. You can now leave <a href="/" title="Home">notes</a>. """ % (name,), _footer] if __name__ == '__main__': # Define the global configuration settings of CherryPy global_conf = { 'global': { 'autoreload.on': False, 'server.socket_host': 'localhost', 'server.socket_port': 8080, 'server.protocol_version': 'HTTP/1.1' }} application_conf = { '/style.css': { 'tools.staticfile.on': True, 'tools.staticfile.filename': os.path.join(_curdir, 'style.css'), } } # Update the global CherryPy configuration cherrypy.config.update(global_conf) # Create an instance of the application note_app = NoteApp() # attach an instance of the Author class to the main application note_app.author = Author() # mount the application on the '/' base path cherrypy.tree.mount(note_app, '/', config = application_conf) # Start the CherryPy HTTP server cherrypy.server.quickstart() # Start the CherryPy engine cherrypy.engine.start() }}} 下面是 CSS 文件的内容, 请在 {{{note.py}}} 同目录下创建 {{{style.css}}} 文 件, 写入下面的内容. {{{ html, body { background-color: #DEDEDE; padding: 0px; margin: 0px; height: 100%; } .container { border-color: #A1A1A1; border-style: solid; border-width: 1px; background-color: #FFF; margin: 10px 150px 10px 150px; height: 100%; width: 400px; } a:link { text-decoration: none; color: #A1A1A1; } a:visited { text-decoration: none; color: #A1A1A1; } a:hover { text-decoration: underline; } input { border: 1px solid #A1A1A1; } .form { margin: 5px 5px 5px 5px; } .info { font-size: 70%; color: #A1A1A1; } }}} 在本章接下来的部分我们会引用这个应用来解释 CherryPy 的设计. == 3.3. 内建 HTTP 服务器 == CherryPy 自带了 web (HTTP) 服务器. 这是为了使 CherryPy 完全可以不需要 其他第三方库就可以独立运行(self-contained), 用户可以在很短时间内搭建好 CherryPy 应用的 环境. 正如名称所暗含的, web 服务器是到 CherryPy 应用的出入口, 所有 HTTP 请求和应答都必须通过它. 因此在这个层上处理低级别的 TCP socket, 在 客户端和服务器间传输信息. CherryPy 并没有强制使用它的内建服务器, 我们可以在需要时使用其他 web 服 务器. 不过在本书中我们只会使用它的内建 web 服务器. 使用下面的调用启动 web 服务器: {{{ cherrypy.server.quickstart() }}} {{{ WARN 在 CP 3.2 中将被废弃, 使用 cherrypy.engine.start() 代替. }}} == 3.4. 内部引擎 == CherryPy 引擎负责管理如下层: * 创建并管理请求和应答对象 * 请求对象负责取回和调用匹配所请求 URI 的页面处理器 * 应答对象在应答被传递到底层服务器前构造并确认它 * 控制, 管理, 监控 CherryPy 进程 使用下面的调用启动引擎: {{{ cherrypy.engine.start() }}} == 3.5. 配置 == CherryPy 自带了一个配置系统, 你可以使用各种参数控制 HTTP 服务器以及 CherryPy 引擎处理请求 URI 时的行为. 有两种方法配置应用. 首先, 配置可以储存在文本文件中, 使用类似 {{{INI}}} 文 件的语法或是纯 Python 字典格式. 你可以任意选择, 这两种方法可以表达相同 的信息. CherryPy 提供两种传递配置值的入口 - 使用 {{{cherrypy.config.update()}}} 方法为服务器实例设置全局配置(server)或是使用 {{{cherrypy.tree.mount()}}} 设置特 定应用的配置(per application). 另外, 你还可以为某一路径设置(per path). 配置 CherryPy 服务器实例你需要在设置中指定 {{{global}}} 块. 在 {{{note}}} 应用中, 我们定义了如下设置: {{{ global_conf = { 'global': { 'autoreload.on': False, 'server.socket_host': '127.0.0.1', 'server.socket_port': 8080, 'server.protocol_version': 'HTTP/1.1' }} application_conf = { '/style.css': { 'tools.staticfile.on': True, 'tools.staticfile.filename': os.path.join(_curdir, 'style.css'), } } }}} 这些内容等同于在配置文件中写入: {{{ [global] server.socket_host = "127.0.0.1" server.socket_port = 8080 [/style.css] tools.staticfile.on = True tools.staticfile.filename = "/full/path/to.style.css" }}} {{{ NOTE 使用文件保存设置的时候, 你必须使用有效 Python 对象 (字符串, 整数, 布尔值等) }}} 我们定义了服务器将要监听传入连接的主机和端口. 然后我们指示 CherryPy 引擎使用 {{{staticfile}}} 工具处理 {{{/style.css}}} 文件, 同时告诉它文件的具体物理路径. 我们将在后面的章节详细解释这些工具, 现在我们只需要认为它可以扩展 CherryPy 的内部特性, 提高处理能力. 为了告知 CherryPy 全局设置, 我们需要执行以下调用: * 如果是字典对象 BR {{{ cherrypy.config.update(conf) }}} * 如果是配置文件 BR {{{ cherrypy.config.update('/path/to/config/file') }}} 我们也可以将设置值传递给已挂载好的应用: * 如果是字典对象 BR {{{ cherrypy.tree.mount(application_instance, script_name, config=conf) }}} * 如果是配置文件 BR {{{ cherrypy.tree.mount(application_instance, script_name, config='/path/to/config/file') }}} 虽然大多情况下在两者间选择只是喜好的问题, 但有时候某个选择会比另种要更 适合实际应用. 例如, 你需要为配置的某个键传递复杂数据或对象, 那么你会发 现文本文件很难满足你的要求. 另一方面, 如果配置可能会被应用的管理员更改, 那么使用 INI 文件会使任务更容易. {{{ NOTE 如果需要配置应用的某个部分, 例如 Note 应用中的样式表, 你必须调用 cherrypy.tree.mount(). }}} 最后一种配置应用的方法是在页面处理器中使用 {{{_cp_config}}}, 或是在包 含页面处理器类中作为类属性使用, 在这种情况下, 该属性影响类中的所有页面 处理器. 在下面的代码例子中, 我们假定 {{{Root}}} 类中除 {{{hello}}} 外的所有其他页面 处理器都会使用{{{gzip}}} 压缩. {{{ import cherrypy class Root: _cp_config = {'tools.gzip.on': True} @cherrypy.expose def index(self): return "welcome" @cherrypy.expose def default(self, *args, **kwargs): return "oops" @cherrypy.expose # 下一行不起任何作用, 因为我们已经设置了类的 _cp_config 属性. # 在一般情况下, 你可以像这样使用修饰器设置 tool. # 我们将在后面章节详细讨论. @cherrypy.tools.gzip() def echo(self, msg): return msg @cherrypy.expose def hello(self): return "there" hello._cp_config = {'tools.gzip.on': False} if __name__== '__main__': cherrypy.quickstart(Root(), '/') }}} 上面对 {{{quickstart}}} 的调用是下面几条命令的快捷方式: {{{ cherrypy.tree.mount(Root(), '/') cherrypy.server.quickstart() cherrypy.engine.start() # 当然, 第二个可以没有. }}} 你可以使用这个调用在 CherryPy 服务器上挂载单个应用. 最重要的一点是对于每个挂载的应用(不同的前缀), 配置是独立的. 所以上面的 例子中, 应用完全可以挂载在 {{{/myapp}}} 上而不是 {{{/}}}, 使用相同的设置. 设置并不包括前缀, 所以你完全可以认为配置对于应用来说是相对的, 对于挂载 应用的前缀来说是独立的. {{{ NOTE 应用挂载到的前缀指的是 script_name. }}} == 3.6. 对象发布引擎 == HTTP 服务器, 例如 Apache 或 lighttpd, 将请求 URI 映射到文件系统上的路 径, 这样可以使得服务器在处理主要由静态内容(例如图片, 纯 HTML 文件)构成 的站点时很有效. CherryPy 选择了一个完全不同的方法, 它使用自己的内部查询算法取出请求 URI 所需的页面处理器. CherryPy 2.0 时就决定, 这样的处理器应该是一个 Python 可调用对象, 附加到一个已发布对象的树上. 这就是我们为什么把对象 发布看作请求 URI 到 Python 对象的映射. CherryPy 定义了两个重要概念: * '''已发布的''': 当一个 Python 对象附加到一个对象树, 且这个树的根对象已 经通过调用 {{{cherrypy.tree.mount}}} 被挂载到了 CherryPy 引擎服务器上. 那么这个对象就是已发布的(Published). BR 例如: BR {{{ root = Blog() root.admin = Admin() cherrypy.tree.mount(root, '/blog') }}} 在上面的例子中, root 对象被认为是已发布的. 将 admin 对象作为属性扩展到 它上, 那么 admin 对象也是已发布的. * '''暴露的''': 当一个已发布对象有 {{{exposed}}} 属性且该属性被设置为 {{{True}}} 时, 我们就说这个对象是暴露的. 一个暴露的对象必须是 Python 可调用对象. BR 已发布并不能保证 Cherrypy 把对象作为 URL 处理器. /* moin code generated by txt2tags 2.5 (http://txt2tags.sf.net) */ /* cmdline: txt2tags -t moin CherryPyBook.t2t */ |
CherryPy Essentials
已挖坑 |
By andelf |
完成度10% |
CherryPy Essentials
Translated By Feather.et.ELF ([email protected])
2008/09/06
1. 目录
- 前言
CherryPy 介绍
下载安装 CherryPy
- 需求
- 概述
- 从 Tarball 安装
- 通过 Easy Install 安装
- 从 Subversion 安装
- 测试安装
随时更新 CherryPy
- 小结
CherryPy 概览
- 词汇表
- 基本例子
- 内建 HTTP 服务器
- 内部引擎
- 配置
- 对象发布引擎
- 库
- 自动导入特性
- 缓存模块
- Coverage 模块
- Encoding/Decoding 模块
- HTTP 模块
- Httpauth 模块
- Profiler 模块
- Sessions 模块
- Static 模块
- Tidy 模块
- Wsgiapp 模块
- XML-RPC 模块
- 工具
- 错误和异常处理
- 小结
深入了解 CherryPy
- HTTP Compliance
- 多重 HTTP 服务器
- 多线程应用服务器
- URI Dispaching
- HTTP 方法 Dispacher
- Routes Dispacher
- 虚拟主机 Dispacher
Hook 到 CherryPy 核心引擎
CherryPy 工具箱
- 基本认证工具
- 缓存工具
- 解码工具
- 摘要认证工具
- 编码工具
- 错误重定向工具
- Etag 工具
- Gzip 工具
- 忽略 Headers 工具
- 记录 Headers 工具
- 记录跟踪返回工具
- 代理工具
- 参考工具
- 请求头工具
- Trailing Slash 工具
- XML-RPC 工具
- 工具箱
- 创建新的工具
- 静态资源服务
- 使用 Staticfile 工具提供单个文件
- 使用 Staticdir 工具提供整个目录
- 绕开 Static 工具提供静态内容
- WSGI 支持
- 小结
- 一个图片 Blog 应用
- 图片 Blog 应用
- 图片 Blog 实体
- 词汇表
- DBMS 概览
- 关系数据库管理系统(RDBMS)
- 面向对象数据库管理系统(OODBMS)
- XML 数据库管理系统(XMLDBMS)
- 对象关系映射
- Python 对象关系映射
- 图片 Blog 实体模型的建立
- 映射实体
- 单位和单位属性
- 联合单位
- 沙箱接口
- 查询单位
- 扩展数据访问层
- 小结
- Web 服务
- 传统 Web 开发
- 分工
- REST
- 统一资源定位符
- HTTP 方法
- 把它们放在一起
CherryPy 的 REST 接口
- Atom 发布协议
- Atom XML 文档格式
- APP 实现
- 小结
- 传统 Web 开发
- 表现层
- HTML
- XML
- XHTML
- CSS
- DHTML
- 模板
- Kid 模板引擎
- 概览
- Kid 属性
- 基于 XML 的模板语言
- 变量替换
- 条件语句
- 循环机制
- 扩展性
- 其他属性
- 图片 Blog 设计表现
- 面向用户代理
- 工具
- 全局设计目标
- 设计目录布局
CherryPy - 封装模板渲染过程
- 图片 Blog 设计细节
- 基本结构
- Mochikit
- 开发图片 Blog 设计
- HTML 代码
- 添加链接
- 处理最终用户行为
- 修改模板
- 修改 CSS
- 可扩展性
- 小结
- Ajax
- 富客户端应用的流行
- Ajax
- Ajax - 优点和缺点
- 在后台: XMLHttpRequest
- 执行 GET 请求
- 执行内容协商 GET 请求
- 执行 POST 请求
- 执行 PUT, HEAD, 或 DELETE 请求
- Cookies
- 使用摘要或基本方案进行认证
- JSON
- 在我们的应用里使用 Ajax
- 定义所需名称空间
- 实现名称空间
- 给类添加方法
- 创建新相册的方法
- 更新已有相册的方法
- 删除已有相册的方法
- 小结
- 测试
- 为什么要测试
- 计划测试
- 常见测试方法
- 单元测试
- unittest
- doctest
- 单元测试 Web 应用
- 功能测试
- 测试下的应用程序
- Selenium Core
- Selenium IDE
- Selenium Remote Control
- 测试下的应用程序
- 小结
- 部署应用
- 索引
2. 前言
在过去几年里, 我们经历了 Internet 大爆发, 许多编程语言都开始提供大量的 web 开发工具, 库, 以及框架.
Python 编程语言同样提供了这些环境, 例如 Zope 和 Twisted , 它们中的很多 只是很小的社区. 这时, CherryPy 出现了, 它的作者...
2.1. 本书包含内容
第一章 CherryPy 背后的故事以及对其的概览
- 第二章
2.2. 你需要准备的东西
本书假定你已经安装好如下程序包.
- Python 2.4 或更高版本
CherryPy 3.0
2.3. 本书的读者
本书原则上面向 web 开发人员. ...
2.4. 格式约定
本书中, 你会发现很多文本样式, 分别用于提供不同的信息. 这里做一简要说明.
代码有三种样式. 文本中的代码单词例如: A newer and more common way of deploying a package is to use the easy_install command to install eggs.
代码块例如:
blody { background-color: #663; clor: #fff; } p { text-align: center; }
命令行输入使用如下格式:
python ez_setup.py
新 术语 和 重要单词 使用黑体字显示. 屏幕上的字, 例如在菜单或是 对话框中, 使用: 下一步, 单击 All 按钮, 运行这些测试.
WARN 警告和重要评注将出现在这里.
TIP 各种 Tips 和小技巧将出现在这里.
2.5. 读者反馈
我们欢迎来自读者的反馈信息. 请让我们知道你对这本书的看法, 无论喜欢与否. 读者反馈信息对我们的进步来说是很重要的, 同时也可以为大家提供更好的服务.
一般反馈信息请发送邮件到 [[email protected]] , 记得在邮件里包含本书 的名字.
如果你需要我们出版某类图书, 请在 [http://www.packtpub.com www.packtpub.com] 站点上的 SUGGEST A TITLE 给我们发送短信, 或是发送邮件到 [[email protected]] .
如果你想就某一擅长的话题写作书籍或是为图书贡献的话, 请参考 [http://www.packtpub.com/authors www.packtpub.com/authors] 上的作者指导.
2.6. 客户支持
现在, 你是一本 Packt 图书的拥有者, 这里有些相关信息帮助你利用好这本书.
2.7. 下载本书的范例代码
请访问 [http://www.packtpub.com/support] , 从列表中选择本书的名字, 下载 本书的范例代码以及其他资料.
所下载文件包含使用说明.
2.8. 勘误
尽管我们尽力确保本书内容的正确性, 但是错误是难免的. 如果你在我们的书里 发现错误 - 正文或是代码 - 请报告给我们, 不胜感谢. 这样会帮助其他读者使 他们不会陷入疑惑中, 同时也有助于我们改进后继版本. 如果你发现任何错误, 请汇报至 [http://www.packtpub.com/support] , 选择书名, 点击 Submit Errata 链接, 输入你所发现错误的细节内容. 一旦确认, 你所提交 的勘误将被加入到现有勘误列表中. (访问该站点你可以获得现有勘误列表).
2.9. 问题
如果你有关于本书的某方面的问题, 你可以联系 [[email protected]] , 我 们会尽力回答你的信件.
3. 1. 介绍 CherryPy
万维网 (World Wide Web) 的使用呈指数级增长, 已经成为我们生活中的一个重 要组成. 从开发者的观点来看, Web 提供了大量的机会和乐趣. 然而, 面向 Web 的技术太过繁杂, 很难决定使用哪个. 本书的目标是展示其中的一种, CherryPy, 一个 Python web 应用库.
本章将介绍 CherryPy 的特性和优点, 首先概括 CherryPy 的历史, 然后介绍它 成功的最大原因 - 友好的社区. 最后回顾下 CherryPy 革命的关键原则.
3.1. 1.1. 概述
CherryPy 为 Python 开发者提供了一个 HTTP 协议的友好接口. HTTP 是 World Wide Web 的支柱. Web 应用在过去几年里快速增长. 随着爆发而来的是各种编 程语言中大量的工具箱, 库, 以及框架. 所有的这些都是为了减少 web 开发人 员的劳动. 在这样的背景下, CherryPy 出现了, 它建立在 Python 的动态特性 上, 将 HTTP 协议绑定到一个符合 Python 习惯的 API 上.
Python 社区开发了大量的 web 库和框架, 以至于出现了个玩笑 "as much as a worry". 尽管其中只有少数吸引了社区的注意 (TurboGears, Django, Zope 等), 但是不可否认的是, 每个库或是框架都有对 Python 与 HTTP 接口的独特 处理思想, 或多或少都有一定的影响. CherryPy 的出现是因为那时候的 Remi Delon , 它的作者, 在已有选择里没有找到他想要的那个. 有很开发者被 CherryPy 的优点吸引, 加入社区为 CherryPy 的设计贡献力量. 今天, 这个项 目有者强大的社区基础, 在不同的环境下将它作为日常使用.
3.2. 1.2. CherryPy 历史
Remi Delon 在 2002 年六月发布了 CherryPy 的第一个版本. 这是一个成功的 Python web 库的起始点. Remi 是一个法国黑客, 他相信 Python 是 web 应用 开发的最终选择.
Remi 的方法吸引了很多开发者.
CherryPy 类是一个 Python 扩展, 它将数据和表现分离开来. 这与 MVC
- (model-view-controller, 模型-视图-控制器) 模式很类似.
CherryPy 类必须被 CherryPy 引擎处理编译, 生成一个自包含的 Python
- 模块, 其中嵌入了整个应用以及它自己内建的 web 服务器.
CherryPy 会把 URL 连同它的查询字符串映射到一个 Python 方法调用, 例如 [http://somehost.net/echo?message=hello] 会被映射到 echo(message='hello') .
在随后的两年里, 这个项目被社区支持, Remi 发布了一些改进版本.
在 2004 年六月, 开始了一场关于项目未来发展以及是否维持结构不变的讨论. 最主要问题之一是编译阶段, 这让 Python 开发者感觉不自然. 集体讨论和关于 一些项目原则的讨论最终发展成为对象发布引擎和过滤器的概念, 这很快成为 CherryPy 2 的一个核心组成.
最后, 在 2004 年十月, CherryPy 2 alpha 的第一个版本发布了, 作为这些核 心概念的证明. 随后六个月, 为了发布一个稳定版本(2005 年四月), 大家都投 入到的紧张工作里. CherryPy 2.0 是一个很大的成功; 不过, 大家也认识到它 的设计仍然可以改进, 它需要重构.
在进一步的社区反馈/讨论后, CherryPy 的 API 做了一些修改, 以使它的结构 更完美, 这便是随后在 2005 年十月发布的 CherryPy 2.1.0 . 该版本被流行的 TruboGears 项目采用 - 它是由一堆项目组成的一个 web mega-框架. 开发团队 在 2006 年四月发布了 CherryPy 2.2.0 .
CherryPy 作为核心组成出现在被广泛采用的 TruboGears 框架上意味着 CherryPy 的一些方面出现了越来越多问题. 例如它的 WSGI 支持, 缺乏最新的 文档, 还有它不是很出众的性能. 在重要的实际需求中, 不破坏向后兼容性去 扩展 CherryPy 2 是很困难的. 最终, 大家决定向 CherryPy 3 前进. CherryPy 于 2006 年十二月被发布.
3.3. 1.3. 社区
在过去几年里, 如果没有社区, CherryPy 就不会有现在这样的成功. Remi 清楚 地知道他并不想让 CherryPy 只是作为自己的私人项目存在, 而是成为一个社区 性质的项目.
CherryPy 一直都有一部分追随者, 但事实上 CherryPy 社区是在版本 2.0 开始 的. 在 2004 年十一月, Open and Free Technology Community (OFTC) 网络上注册了一个 IRC 频道, 开发者和用户可以通过它快速交换想 法, 或是报告错误. 这个频道慢慢吸引了越来越多的长期用户, 被广泛认为是一 个友好的地方. 除 IRC 频道外, 针对开发者和用户的邮件列表也被创建. 还有 一个 CherryPy 用户的 blog feed 聚合站点也建立了起来, 你可以访问 [http://planet.cherrypy.org] .
3.4. 1.4. CherryPy 项目优点
简单: 使 CherryPy 尽可能简单是主要目标之一. 这可以避免库 over
- engineering 项目. 由于库本身的范围比较窄, 开发者可以把注意力集中在 API 和社区反馈信息上.
自包含(self-contained): 从一开始, Remi 就决定 CherryPy
的核心不会需要任何第三方 Python 库, CherryPy 完全依赖于 Python 标准库.
非强制性: 确保 CherryPy 和其用户的方式一致,
- 提供的工具没有假定开发者的特定使用方式.
- 开放式讨论: 开发团队会听取社区的反馈. 这并不意味着每个要求都被采纳,
- 不过大多数都会被仔细讨论评价.
- 有趣: 开发开源项目时, 贡献者不会觉得这只是他们的日常工作;
事实上他们会以此为乐. 同样地, 对于 CherryPy 用户来说, 趣味同样是一个重要部分, 发现这一点会让我们都成为更好更有创造力的开发人员.
3.5. 1.5. 超越 CherryPy
在一开始, CherryPy 吸引了一小群用户, 但它的设计导致它不能被更多人接受 或是更广泛使用. 此外, 在那时, Python web 开发领域几乎完全被 Zope 平台 占据. CherryPy 2 发布时, 它的概念被社区热烈欢迎, 吸引了更多的用户在应 用中使用 CherryPy 或是基于它建立他们自己的包.
在 2005 年五月, Kevin Dangoor 发布了 TruboGears - 使用一堆开源产品建立 的一个 web 开发框架. 在框架里 Kevin 选择了 CherryPy 处理 HTTP 层, SQLObject 将对象映射到数据库, Kid 用做 XHTML 模板, MochiKit 作为客户端 处理. 这只是在另个 Python web 框架, Django 向社区开放的几个月之后. 这 两个项目都很快在 Python 社区流行起来, 由于它们之间的小小竞争, 使得流行 趋势更加迅速. TurboGears 的急速发展也推进了 CherryPy 知名度, 吸引了大 量的新用户.
这些新开发者为 CherryPy 增加了许多新特性, 同时也修复了一些缺点, 最终作 为 CherryPy 3 发布, 至本书完成时候库的最稳定版本.
CherryPy 的未来是明确的; Robert Brewer 所做的杰出工作使得库的处理速度 有了很大提高. TurboGears 的未来版本将会迁移到 CherryPy 3 上, 这会为开 发团队带来新的需要解决的问题, 也会让 CherryPy 继续前进.
3.6. 1.6. 全书概览
本书旨在介绍 CherryPy , 使你有信心在你自己的 web 应用中把它用好. 另外 我们也将展开对 web 应用设计的讨论. 首先本书将介绍 Python 社区使用的一 些获得安装 CherryPy 的常见方法, 例如使用 setup tools 和 easy_install. 同时你将大致了解 CherryPy 的主要概念, 逐渐明白这个库可以 做什么. 然后我们深入到库的一些特性, 例如 HTTP 能力, URI 调度器, 如何扩 展库, 以及它的 WSGI 支持.
此时, 你会对 CherryPy , 它的设计, 以及如何使用它有深入的了解. 然后, 本 书将通过一个简单的 blog 应用介绍各种技术和工具, 例如对象关系映射, web 服务, 以及 Ajax , 使你对 web 开发的各个层有一定的的了解.
我们将提出 blog 应用的目标和边界(x), 回顾 Python 数据库处理的情况, 解 释对象关系映像. 这里我们会展示 Python ORM 之一, Dejavu. 本书还会讨论 REST 和 Atom 发行协议(Atom Publishing Protocol), 它们提供了扩可展 web 应用的设计方法. 然后我们介绍 blog 应用的表现层, 它使用了 Kid 模板引擎 和 MockiKit JavaScript 库. 随后本书简单讨论了 Ajax 以及如何使你的应用 受益于它. 我们还会看到你的应用如何才能调用 web 服务. 然后, 本书会介绍 web 应用测试领域. 这来自于单元测试, 它通过其中的函数测试概念载入测试. 本书最后会介绍一些部署 web 应用到独立应用或是常见 web 服务器(例如 Apache, lighttpd)的方法.
虽然其中一些扩展章节并没有讨论 CherryPy 本身, 但是所有章节都以介绍 web 应用开发为核心. 本书教你使用 CherryPy , 你将有基础和动力继续学习更多相关话题.
3.7. 1.7. 小结
在读完这些介绍后, 你应该对本书一些了解. CherryPy 是一个简单且强大的 Python 库, 如果你寻找一个把 HTTP 协议中艰涩的部分封装在底层却同时不失 其优点的库, 那么 CherryPy 是你的正确选择. CherryPy 社区在过去的时间里 努力使这样一个产品成为可能; 本书会给你正确的指导, 让你利用好它.
4. 2. 下载安装 CherryPy
和大多开源项目类似, CherryPy 可以通过多种方法下载安装. 这里我们将介绍 下面三种方法:
- 使用 tarball
- 使用 easy_install
- 从 Subversion 获得最新版本源代码
每种方法都会为这个项目的用户带来不同的价值, 了解这点很重要.
当你读到这一章时, 你应该可以取得并部署 CherryPy , 同时知道如何在你自己 的软件里使用这些技术.
4.1. 2.1. 需求
本书通篇都假设你安装好了这些程序包:
- Python 2.4 或更高版本
CherryPy 3.0
同时我们假定你已经对 Python 本身有足够的了解, 本书将不会涵盖语言本身的 内容.
4.2. 2.2. 概述
安装 Python 模块或包通常只是很简单的过程. 首先我们讨论最常见的建立和安 装新的包的方法, 多亏有了 Python 2.0 中被加入的一个标准模块 distutils .
该模块提供了一个描述包结构的简洁接口, 包的需求关系, 编译规则都在其中. 对于用户来说, 通常只意味着输入命令:
python setup.py build python setup.py install
第一条命令使用开发者指定的规则编译包, 在依存关系不满足的时候把错误汇报 给最终用户. 第二条命令将包安装到 Python 的默认第三方包/模块保存目录. 注意第二条命令会调用第一条命令来做快速检查, 确保在最后一次运行后没有任 何改动发生.
包和模块的默认位置在:
- 在 UNIX 或 Linux 下
/usr/local/lib/python2.4/site-packages 或 /usr/lib/python2.4/site-packages
- 在 Microsoft Windows 下
C:\Python\lib\site-packages 或 C:\Python2X\lib\site-packages
- 在 MacOS 下
Python:Lib:site-packages
在 UNIX 或 Linux 下它可能因为你的 Python 安装位置不同而不同, 但是这些 目录路径几乎都是相似的. 导入模块时, Python 会在一系列目录中查找模块, 其中一些是默认的, 另一些则由用户提供, 直到它发现一个匹配的模块, 否则会 引发一个异常. 修改搜索列表可以通过定义 PYTHONPATH 环境变量或是直 接在代码中进行修改:
import sys sys.path.append(path)
NOTE PYTHONPATH 环境变量是 Python 引擎启动时读取的环境变量之一. 它包含需要加入搜索列表的地三方模块和包的路径.
另个方法是创建一个和包同名的文件, 以 .pth 作为扩展名. 该文件内写入 包的完整路径.
虽然这很简洁, 但这个方法有它的局限性. 由于 sys.path 列表是有序的, 如果两个路径包含相同模块的不同版本, 那么你必须保证你的应用中导入的那个 可以被首先访问到. 这导致我们陷入包的版本问题.
假设你在全局安装的 Python 里安装了 CherryPy 2.2.1 , 它会被安装到 /usr/local/lib/site-packages/cherrypy 中. 然而, 路径并未包含任何版 本信息, 如果你必须安装 CherryPy 3.0.0 , 那么你必须覆盖已有安装的版本.
幸运的是, Python 社区提供了一个解决方法 - eggs . egg 是一个压缩的 文件夹, 连同包内的所有文件和子目录. 它的文件名含有包的版本细节.
NOTE egg 是可分发的压缩包, 它含有 Python 包或模块的相关信息(例如作者, 版本等).
例如, Python 2.4 编译 CherryPy 2.2.1 会得到文件 Cherrypy-2.2.1-py2.4.egg . egg 自身并不是很有用; 它的部署需要 easy_install , 一个包含处理 egg 逻辑的 Python 模块. 这意味着你可以在同 个目录下部署不同版本, 让 easy_install 去决定导入哪个版本.
下一章节我们将看看在通常情况下如何安装 CherryPy .
4.3. 2.3. 从 Tarball 安装
Tarball 是压缩的归档文件. 文件名来自于 UNIX 及关联系统的 tar 工具.
NOTE 最广泛使用的是 gzip 工具, tarball 的扩展名为 .tar.gz 或是 .tgz.
CherryPy 为每个发布都提供了一个 tarball, 无论是 alpha, beta, release condidate, 或是稳定版本. 你都可以从 [http://download.cherrypy.org/] 获得.
CherryPy tarball 包含库的完整源代码.
从 tarball 安装 CherryPy 需要如下几步:
从 [http://download.cherrypy.org/] 下载你感兴趣的版本
- 找到文件存放位置, 解压缩之:
- 在 Linux 下, 使用如下命令: BR
tar zxvf cherrypy-x.y.z.tgz
- 其中 x.y.z 是你所获得的版本号.
- 如果你在 Microsoft Windows 下, 你可以使用 7-zip 等解压缩工具在图形
- 界面下解压文件.
移动到解压出的文件夹, 输入如下命令编译 CherryPy BR
python setup.py build
- 最后, 使用如下命令创建全局安装(你可能需要管理员权限):
python setup.py install
NOTE 这些命令必须使用命令行输入. 在 Microsoft Windows 下你需要执行 DOS 命令行.
以上几步会为创建系统默认 Python 环境的全局安装. 有时候你可能并不想这样 或者不能这样. 例如你可能不只为一个版本的 Python 安装 CherryPy; 这时候 你需要在第 3 步或第 4 步指定正确的 Python 文件.
或者你可能不想在全局环境里安装, 如果在 UNIX 下你只需要把第 4 步替换为:
python setup.py install --home=~
这会将文件放置在 $HOME/lib/python, 其中 $HOME 是你的主目录.
在 Microsoft Windows, 下不存在 HOME, 你应该使用如下命令:
python setup.py install --prefix=c:\some\path
路径自身并不重要, 你可以使用符合你的环境的任意路径.
然后你必须确认 Python 在你需要导入模块的时候检查该路径. 最简单的方法是 设置 PYTHONPATH 环境变量:
- 在 Linux 下使用 bash shell 执行: BR
export PYTHONPATH=~/lib/python
- 在 Microsoft Windows 下使用命令行输入: BR
set PYTHONPATH=/some/path
TIP 注意这只会对你当前打开的命令行窗口有效, 在关闭窗口后会被丢弃. 若想永久改变环境变量, 请通过 系统属性 | 高级 | 环境变量 设置.
- 在 MacOS 下使用 csh shell 输入: BR
setenv PYTHONPATH "/some/path"
PYTHONPATH 环境变量会在 Python 解释器启动的时候被读取, 自动加入它 的内部系统路径(sys.path).
4.4. 2.4. 通过 Easy Install 安装
easy_install 模块提供了增强的 Python 包和模块部署能力, 你可以在 Python Enterprise Application Kit(PEAK) 网站得到它. 从开发者的观点来看, 它提供了一个很简单的模块导入 API, 你可以轻松地导入 指定版本或是指定某一版本范围. 例如, 你可以通过如下代码导入在环境中可找 到的第一个版本号大于 2.2 的 CherryPy :
>>> from pkg_resources import require >>> require("cherrypy>=2.2") [Cherrypy 2.2.1 (/home/sylvain/lib/python/ Cherrypy-2.2.1-py2.4.egg)]
从用户的观点看, 它简化了下载, 编译, 部署 Python 的过程.
在安装 CherryPy 之前, 我们必须安装 easy_install 本身. 从 [http://peak.telecommunity.com/dist/ez_setup.py] 下载 ez_setup.py 模 块, 然后使用有管理权限的用户执行:
python ez_setup.py
如果你没有管理员权限, 你可以使用 -install-dir (-d) 选项:
python ez_setup.py -install-dir=/some/path
确保 /some/path 在 Python 的系统路径中. 你可以把 PYTHONPATH 设 置为该目录.
这样之后, 你的环境就支持 easy_install 了. 然后安装支持 easy_install 的 Python 产品, 你只需要如下命令:
easy_install product_name
easy_install 会在 Python Package Index(PyPI) 搜索指定产品. PyPI 是一个 Python 产品信息的主要仓库.
为了部署 CherryPy 的最新可用版本, 你需要执行以下命令:
easy_install cherrypy
easy_install 会下载 CherryPy , 编译, 然后安装到全局 Python 环境. 如果 你需要把它安装到指定位置, 请输入如下命令:
easy_install --install-dir=~ cherrypy
安装后, 你会在指定目录得到一个名称为 cherrypy.x.y.z-py2.x.egg 的文 件, 具体文件名取决于当前 CherryPy 的最新版本号.
4.5. 2.5. 从 Subversion 安装
Subversion 是一个很优秀的开源版本控制系统, 开发者可以通过它安全有效地 控制项目.
这样的系统的基本原则是注册一个资源, 然后记录它的每次更改, 这样开发者就 可以恢复到任意一个先前版本, 比较两个版本, etc. 资源可以是源代码文件, 二进制文件, 图像, 文档, 或是其他机器可读的格式.
Subversion 是自动的, 也就是说如果一个 commit 失败, 那么整个 commit 就 算是失败的. 另一方面说, 如果它成功的话, 整个项目的版本号会增加, 而不是 只涉及这个文件.
NOTE Subversion 一般被看做 CVS 的继承者, 它更友好. 当然, 还存在着其他版本管理系统, 例如 Monotone 或 Darcs.
在 Linux, 你可以从源代码安装 Subversion 或是使用软件包管理器. 我们来看 看如果从源代码安装的过程.
从 [http://subversion.tigris.org/] 获得最新的 tarball
- 然后在命令行里输入命令: BR
tar zxvf subversion-x.y.z.tar.gz
在解压后得到的目录地输入: ./configuure
使用 make 命令编译包.
- 你可能会用到 Python 的 Subversion 绑定: BR
make swig-py
- 全局安装 Subversion , 你需要在管理员权限下执行: BR
make install; make install-swig-py
大多情况下, 在 Linux 或是 UNIX 系统中, 通过命令行使用 Subversion 是很 简单的. 当然, 如果你喜欢图形界面的话, 我推荐你安装客户程序, 例如 eSvn 或是 kdesvn.
在 Microsoft Windows 下, 直接使用图形应用程序更简单, 例如 TortoiseSVN, 当然, 它也会安装 Subversion 客户端.
在以下情况推荐使用 Subversion 安装 CherryPy :
- 某一特性或是 bug 修复只在开发中代码存在
你决定研究 CherryPy 本身
- 你需要从主干上分出一个分支来实现一个新特性, 新设计, 或是在先前版本中
- 修复 bug
为了使用该项目的最新版本, 你需要检查仓库的 trunk 目录. 在 shell 下输入 下面的命令:
svn co http://svn.cherrypy.org/trunk cherrypy
TIP 在 Microsoft Windows 下, 你可以通过命令行或是直接通过 TortoiseSVN 的图形界面取得代码. 具体请参阅它的文档.
这将创建一个 cherrypy 目录, 然后将完整源代码下载到里面. 通常并不推 荐将开发中的版本部署到全局环境里; 你需要使用如下命令将 CherryPy 安装到 一个本地目录:
- 在 Linux 及相关系统下, 使用命令行输入: BR
python setup.py install --home=~
- 在 Microsoft Windows 下使用命令行输入: BR
python setup.py install --prefix=c:\some\path
然后, 记得设置好你的 PYTHONPATH 环境变量.
注意, 目录并不重要, 只要它能被 Pyhon 处理, 无论是使用环境变量还是通过 标准的 sys 模块, 都是可以的.
4.6. 2.6. 测试你的安装
无论你打算用什么方法把 CherryPy 安装部署到你的环境里, 你必须能够从 Python 命令行导入它:
>>> import cherrypy >>> cherrypy.__version__ '3.0.0'
如果你没有把 CherryPy 安装到全局环境下, 别忘记设置环境变量, 否则你将得 到如下的错误信息:
>>> import cherrypy Traceback (most recent call last): File "<stdin>", line 1, in ? ImportError: No module named cherrypy
4.7. 2.7. 更新CherryPy
更新或是升级 CherryPy 的方法取决于你是如何安装它的.
- 使用 tarball 安装 BR
- 确保升级成功的最干净的方法就是从含有包的目录里删除整个包, 然后使用前 边介绍的方法安装库
- 使用 easy_install 安装 BR
- 升级是 easy_install 提供的重要特性之一. BR
easy_install -U cherrypy
- 由于 egg 包含库和其版本信息, 你完全可以忽视已存在的 egg 去安装新的 egg. 不过请确保应用在执行的时候导入的是它需要的包.
- 使用 Subversion 安装
- 这种方法的一个好处是你几乎可以连续地升级整个库. 你需要在保存源代码的
目录输入 svn update 命令, 然后执行 python setup.py install 覆盖已有安装.
- 这种方法的一个好处是你几乎可以连续地升级整个库. 你需要在保存源代码的
NOTE 在升级前记得备份你的文件, 这几乎总是正确的.
4.8. 2.8. 小结
我们在本章讨论了安装 CherryPy 的三种方法. 传统方法是使用包含 Python 包 的所有文件的归档安装, 执行归档文件里提供的 setup.py 文件. 更常见的 方法是使用 easy_install 安装 egg. 最后, 如果你想和 CherryPy 的最新 开发保持同步的话, 你可以从它的 Subversion 仓库获得包. 无论使用哪种方法, 你都可以得到一份可用的 CherryPy.
5. 3. CherryPy 概览
在第一章我们简单介绍了 CherryPy 的一些概念;现在我们深入了解下该项目 是如何设计和构造的. 首先我们将讨论一个简单的 CherryPy 例子. 然后讨论 CherryPy 核心, 对象发布引擎, 以及它是如何将 HTTP 协议封装到一个面向对 象的库里的. 下一步我们将核心钩子(hook), CherryPy 库, 以及工具机制. 然后我们会回顾 CherryPy 的错误和异常处理, 以及如何使用它.
学完本章后, 你将对 CherryPy 库有一个很好的了解; 在以后的学习中你可能需 要再回顾本章的内容.
5.1. 3.1. 词汇表
为了避免误解, 我们首先定义一些在本书会出现的关键词.
- web 服务器
- web 服务器是处理 HTTP 协议的接口. 它将传入的 HTTP 请求转换为传入应用服
- 务器的实体, 同时将来自应用服务器的信息转换并返回到 HTTP 应答中.
- 应用
- 应用是接受信息单位的软件, 应用各种逻辑后, 返回处理后的信息单位.
- 应用服务器
- 应用服务器是为一个或多个应用提供服务平台的部件.
- web 应用服务器
- web 应用服务器是 web 服务器和应用服务器的集合体.
CherryPy 是一个 web 应用服务器.
5.2. 3.2. 基本例子
为了展示 CherryPy 库, 我们将讨论一个很基本的 web 应用, 用户可以通过 HTML 表单在主页面上发表评论. 评论按照创建时间倒序排列. 我们将使用 session 对象来保存评论作者的名字.
每条评论都有一个 URI, 类似 /note/id 的格式.
创建一个名为 note.py 的空文件, 写入如下代码.
# -*- coding: utf-8 -* # Python standard library imports import os.path import time ############################################################### #The unique module to be imported to use cherrypy ############################################################### import cherrypy # CherryPy needs an absolute path when dealing with static data _curdir = os.path.join(os.getcwd(), os.path.dirname(__file__)) # We will keep our notes into a global list _notes = [] ############################################################### # A few HTML templates ############################################################### _header = """ <html> <head> <title>Random notes</title> <link rel="stylesheet" type="text/css" href="/style.css"></link> </head> <body> <div class="container">""" _footer = """ </div> </body> </html>""" _note_form = """ <div class="form"> <form method="post" action="post" class="form"> <input type="text" value="Your note here..." name="text" size="40"></input> <input type="submit" value="Add"></input> </form> </div>""" _author_form = """ <div class="form"> <form method="post" action="set"> <input type="text" name="name"></input> <input type="submit" value="Switch"></input> </form> </div>""" _note_view = """ <br /> <div> %s <div class="info">%s - %s <a href="/note/%d">(%d)</a></div> </div>""" ############################################################### # Our only domain object (sometimes referred as to a Model) ############################################################### class Note(object): def __init__(self, author, note): self.id = None self.author = author self.note = note self.timestamp = time.gmtime(time.time()) def __str__(self): return self.note ############################################################### # The main entry point of the Note application ############################################################### class NoteApp: """ The base application which will be hoested by CherryPy """ # Here we tell CherryPy we will enable the session # from this level of the tree of published objects # as well as its sub-levels _cp_config = { 'tools.sessions.on': True } def _render_note(self, note): """Helper to render a note into HTML""" return _note_view % (note, note.author, time.strftime("%a, %d %b %Y %H:%M:%S", note.timestamp), note.id, note.id) @cherrypy.expose def index(self): # Retrieve the author stored in the current session # None if not defined author = cherrypy.session.get('author', None) page = [_header] if author: page.append(""" <div><span>Hello %s, please leave us a note. <a href="author">Switch identity</a>.</span></div>""" % (author,)) page.append(_note_form) else: page.append("""<div><a href="author">Set your identity</a></span></div>""") notes = _notes[:] notes.reverse() for note in notes: page.append(self._render_note(note)) page.append(_footer) # Returns to the CherryPy server the page to render return page @cherrypy.expose def note(self, id): # Retrieve the note attached to the given id try: note = _notes[int(id)] except: # If the ID was not valid, let's tell the # client we did not find it raise cherrypy.NotFound return [_header, self._render_note(note), _footer] @cherrypy.expose def post(self, text): author = cherrypy.session.get('author', None) # Here if the author was not in the session # we redirect the client to the author form if not author: raise cherrypy.HTTPRedirect('/author') note = Note(author, text) _notes.append(note) note.id = _notes.index(note) raise cherrypy.HTTPRedirect('/') class Author(object): @cherrypy.expose def index(self): return [_header, _author_form, _footer] @cherrypy.expose def set(self, name): cherrypy.session['author'] = name return [_header, """ Hi %s. You can now leave <a href="/" title="Home">notes</a>. """ % (name,), _footer] if __name__ == '__main__': # Define the global configuration settings of CherryPy global_conf = { 'global': { 'autoreload.on': False, 'server.socket_host': 'localhost', 'server.socket_port': 8080, 'server.protocol_version': 'HTTP/1.1' }} application_conf = { '/style.css': { 'tools.staticfile.on': True, 'tools.staticfile.filename': os.path.join(_curdir, 'style.css'), } } # Update the global CherryPy configuration cherrypy.config.update(global_conf) # Create an instance of the application note_app = NoteApp() # attach an instance of the Author class to the main application note_app.author = Author() # mount the application on the '/' base path cherrypy.tree.mount(note_app, '/', config = application_conf) # Start the CherryPy HTTP server cherrypy.server.quickstart() # Start the CherryPy engine cherrypy.engine.start()
下面是 CSS 文件的内容, 请在 note.py 同目录下创建 style.css 文 件, 写入下面的内容.
html, body { background-color: #DEDEDE; padding: 0px; margin: 0px; height: 100%; } .container { border-color: #A1A1A1; border-style: solid; border-width: 1px; background-color: #FFF; margin: 10px 150px 10px 150px; height: 100%; width: 400px; } a:link { text-decoration: none; color: #A1A1A1; } a:visited { text-decoration: none; color: #A1A1A1; } a:hover { text-decoration: underline; } input { border: 1px solid #A1A1A1; } .form { margin: 5px 5px 5px 5px; } .info { font-size: 70%; color: #A1A1A1; }
在本章接下来的部分我们会引用这个应用来解释 CherryPy 的设计.
5.3. 3.3. 内建 HTTP 服务器
CherryPy 自带了 web (HTTP) 服务器. 这是为了使 CherryPy 完全可以不需要 其他第三方库就可以独立运行(self-contained), 用户可以在很短时间内搭建好 CherryPy 应用的 环境. 正如名称所暗含的, web 服务器是到 CherryPy 应用的出入口, 所有 HTTP 请求和应答都必须通过它. 因此在这个层上处理低级别的 TCP socket, 在 客户端和服务器间传输信息.
CherryPy 并没有强制使用它的内建服务器, 我们可以在需要时使用其他 web 服 务器. 不过在本书中我们只会使用它的内建 web 服务器.
使用下面的调用启动 web 服务器:
cherrypy.server.quickstart()
WARN 在 CP 3.2 中将被废弃, 使用 cherrypy.engine.start() 代替.
5.4. 3.4. 内部引擎
CherryPy 引擎负责管理如下层:
- 创建并管理请求和应答对象
- 请求对象负责取回和调用匹配所请求 URI 的页面处理器
- 应答对象在应答被传递到底层服务器前构造并确认它
控制, 管理, 监控 CherryPy 进程
使用下面的调用启动引擎:
cherrypy.engine.start()
5.5. 3.5. 配置
CherryPy 自带了一个配置系统, 你可以使用各种参数控制 HTTP 服务器以及 CherryPy 引擎处理请求 URI 时的行为.
有两种方法配置应用. 首先, 配置可以储存在文本文件中, 使用类似 INI 文 件的语法或是纯 Python 字典格式. 你可以任意选择, 这两种方法可以表达相同 的信息.
CherryPy 提供两种传递配置值的入口 - 使用 cherrypy.config.update() 方法为服务器实例设置全局配置(server)或是使用 cherrypy.tree.mount() 设置特 定应用的配置(per application). 另外, 你还可以为某一路径设置(per path).
配置 CherryPy 服务器实例你需要在设置中指定 global 块.
在 note 应用中, 我们定义了如下设置:
global_conf = { 'global': { 'autoreload.on': False, 'server.socket_host': '127.0.0.1', 'server.socket_port': 8080, 'server.protocol_version': 'HTTP/1.1' }} application_conf = { '/style.css': { 'tools.staticfile.on': True, 'tools.staticfile.filename': os.path.join(_curdir, 'style.css'), } }
这些内容等同于在配置文件中写入:
[global] server.socket_host = "127.0.0.1" server.socket_port = 8080 [/style.css] tools.staticfile.on = True tools.staticfile.filename = "/full/path/to.style.css"
NOTE 使用文件保存设置的时候, 你必须使用有效 Python 对象 (字符串, 整数, 布尔值等)
我们定义了服务器将要监听传入连接的主机和端口.
然后我们指示 CherryPy 引擎使用 staticfile 工具处理 /style.css 文件, 同时告诉它文件的具体物理路径. 我们将在后面的章节详细解释这些工具, 现在我们只需要认为它可以扩展 CherryPy 的内部特性, 提高处理能力.
为了告知 CherryPy 全局设置, 我们需要执行以下调用:
- 如果是字典对象 BR
cherrypy.config.update(conf)
- 如果是配置文件 BR
cherrypy.config.update('/path/to/config/file')
我们也可以将设置值传递给已挂载好的应用:
- 如果是字典对象 BR
cherrypy.tree.mount(application_instance, script_name, config=conf)
- 如果是配置文件 BR
cherrypy.tree.mount(application_instance, script_name, config='/path/to/config/file')
虽然大多情况下在两者间选择只是喜好的问题, 但有时候某个选择会比另种要更 适合实际应用. 例如, 你需要为配置的某个键传递复杂数据或对象, 那么你会发 现文本文件很难满足你的要求. 另一方面, 如果配置可能会被应用的管理员更改, 那么使用 INI 文件会使任务更容易.
NOTE 如果需要配置应用的某个部分, 例如 Note 应用中的样式表, 你必须调用 cherrypy.tree.mount().
最后一种配置应用的方法是在页面处理器中使用 _cp_config, 或是在包 含页面处理器类中作为类属性使用, 在这种情况下, 该属性影响类中的所有页面 处理器.
在下面的代码例子中, 我们假定 Root 类中除 hello 外的所有其他页面 处理器都会使用gzip 压缩.
import cherrypy class Root: _cp_config = {'tools.gzip.on': True} @cherrypy.expose def index(self): return "welcome" @cherrypy.expose def default(self, *args, **kwargs): return "oops" @cherrypy.expose # 下一行不起任何作用, 因为我们已经设置了类的 _cp_config 属性. # 在一般情况下, 你可以像这样使用修饰器设置 tool. # 我们将在后面章节详细讨论. @cherrypy.tools.gzip() def echo(self, msg): return msg @cherrypy.expose def hello(self): return "there" hello._cp_config = {'tools.gzip.on': False} if __name__== '__main__': cherrypy.quickstart(Root(), '/')
上面对 quickstart 的调用是下面几条命令的快捷方式:
cherrypy.tree.mount(Root(), '/') cherrypy.server.quickstart() cherrypy.engine.start() # 当然, 第二个可以没有.
你可以使用这个调用在 CherryPy 服务器上挂载单个应用.
最重要的一点是对于每个挂载的应用(不同的前缀), 配置是独立的. 所以上面的 例子中, 应用完全可以挂载在 /myapp 上而不是 /, 使用相同的设置. 设置并不包括前缀, 所以你完全可以认为配置对于应用来说是相对的, 对于挂载 应用的前缀来说是独立的.
NOTE 应用挂载到的前缀指的是 script_name.
5.6. 3.6. 对象发布引擎
HTTP 服务器, 例如 Apache 或 lighttpd, 将请求 URI 映射到文件系统上的路 径, 这样可以使得服务器在处理主要由静态内容(例如图片, 纯 HTML 文件)构成 的站点时很有效.
CherryPy 选择了一个完全不同的方法, 它使用自己的内部查询算法取出请求 URI 所需的页面处理器. CherryPy 2.0 时就决定, 这样的处理器应该是一个 Python 可调用对象, 附加到一个已发布对象的树上. 这就是我们为什么把对象 发布看作请求 URI 到 Python 对象的映射.
CherryPy 定义了两个重要概念:
已发布的: 当一个 Python 对象附加到一个对象树, 且这个树的根对象已
经通过调用 cherrypy.tree.mount 被挂载到了 CherryPy 引擎服务器上. 那么这个对象就是已发布的(Published). BR 例如: BR
root = Blog() root.admin = Admin() cherrypy.tree.mount(root, '/blog')
在上面的例子中, root 对象被认为是已发布的. 将 admin 对象作为属性扩展到 它上, 那么 admin 对象也是已发布的.
暴露的: 当一个已发布对象有 exposed 属性且该属性被设置为
True 时, 我们就说这个对象是暴露的. 一个暴露的对象必须是 Python 可调用对象. BR 已发布并不能保证 Cherrypy 把对象作为 URL 处理器.
::@andelf@ [@080629@] PageComment2