文章目录
  1. 1. 建立buildout.cfg
  2. 2. buildout.cfg配置
  3. 3. 构建环境
  4. 4. 生成的python解释器
  5. 5. 需要注意的地方

前段时间翠翠同学问我有没有接触过buildout这个东西,说是用来构建python环境的。我以前从来没有用过这个,之前很长的一段时间里都是用virtualenv来做python环境的隔离,每个项目建一个,防止第三方库的版本问题。不过既然有提到这个东西那么我也去瞧瞧这货是个怎么样的原理,结果找来找去就几篇中文资料介绍这个的,而且讲的都不全部,很多地方都一笔带过,有点看不太明白。官方的文档写的也不咋滴,只好自己硬着头皮去试,最后大致上是理解了一些。

建立buildout.cfg

首先呢buildout是依靠项目目录下的buildout.cfg文件来安装依赖的包和生成相关配置的。先来安装一下zc.buildout吧。

1
pip install zc.buildout

直接在主环境下执行这个安装就行了,用easy_install也可以。

安装完成之后在项目目前中使用 buildout init 命令就可以生成配置文件和相关目录。做版本管理的时候只要把buildout.cfg文件加入git中基本就行了。

buildout.cfg配置

我自己的一个项目的buildout.cfg文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
; buildout这个段必需存在
[buildout]
; develop里面可以指定自己开发的egg,放在develop-eggs目录下,具体的我也没测试过。
; develop = myegg
develop =
; parts 里面可以放多个段的名字,名字自定义,具体设置写在对应的配置段里面,比如 [tools]
parts = tools
; 这个是指pypi的镜像地址可以不写,默认是官网的。
index = https://pypi.python.org/simple
; 这个指定的版本信息
versions = myversions

; 对应parts里面的设置
[tools]
; recipe 可以理解为配置项的解释器,这个可以自己实现,也可以使用现成的。这项决定了下面的配置项的功能实现。
recipe = zc.recipe.egg:script
; 这里是eggs包的名称
eggs =
; 这里还有一种写法是像下面这个样子
; Flask==0.10.1
; 直接将版本信息写在这里也是可以的
Flask
Flask-Cache
Flask-DebugToolbar
Flask-SQLAlchemy
Flask-Script
Jinja2
MarkupSafe
SQLAlchemy
Werkzeug
blinker
itsdangerous
psycopg2
wsgiref
Plim

; 这个是扩展的引入路径,一般我会加上这个确保当前目录也在引用范围内
; 因为如果不加这个,那么当前目录下的一个模块的引用会提示找不到路径
extra-paths = .
; 指定了生成的python解释器的名称,位置在项目的bin目录下面
interpreter = python

; 这个是具体的版本信息
[myversions]
; 每个安装包的版本,如果在这里指定,那么生成的python解释器就会引用对应的版本。
Flask = 0.10.1
Flask-Cache = 0.12
Flask-DebugToolbar = 0.8.0
Flask-SQLAlchemy = 1.0
Flask-Script = 0.6.2
Jinja2 = 2.7.1
MarkupSafe = 0.18
SQLAlchemy = 0.8.2
Werkzeug = 0.9.4
blinker = 1.3
itsdangerous = 0.23
psycopg2 = 2.5.1
wsgiref = 0.1.2
Plim = 0.8.9

构建环境

网上找到的资料都说是用 buildout bootstrap ,但在我这边无效,我这里用 buildout install 命令可以正常的构建。

生成的python解释器

比如刚刚上面的这个配置文件生成后的python解释器对应的文件就是在 bin/python ,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#!/usr/bin/python

import sys

# 在这里导入了对应版本的egg包
sys.path[0:0] = [
'/Volumes/dev/dev/python/pitayacd_py/eggs/Flask-0.10.1-py2.7.egg',
'/Volumes/dev/dev/python/pitayacd_py/eggs/Flask_Cache-0.12-py2.7.egg',
'/Volumes/dev/dev/python/pitayacd_py/eggs/Flask_DebugToolbar-0.8.0-py2.7.egg',
'/Volumes/dev/dev/python/pitayacd_py/eggs/Flask_SQLAlchemy-1.0-py2.7.egg',
'/Volumes/dev/dev/python/pitayacd_py/eggs/Flask_Script-0.6.2-py2.7.egg',
'/Volumes/dev/dev/python/pitayacd_py/eggs/Jinja2-2.7.1-py2.7.egg',
'/Volumes/dev/dev/python/pitayacd_py/eggs/MarkupSafe-0.18-py2.7-macosx-10.9-intel.egg',
'/Volumes/dev/dev/python/pitayacd_py/eggs/SQLAlchemy-0.8.2-py2.7-macosx-10.9-intel.egg',
'/Volumes/dev/dev/python/pitayacd_py/eggs/Werkzeug-0.9.4-py2.7.egg',
'/Volumes/dev/dev/python/pitayacd_py/eggs/blinker-1.3-py2.7.egg',
'/Volumes/dev/dev/python/pitayacd_py/eggs/itsdangerous-0.23-py2.7.egg',
'/Library/Python/2.7/site-packages',
'/Volumes/dev/dev/python/pitayacd_py/eggs/wsgiref-0.1.2-py2.7.egg',
'/Volumes/dev/dev/python/pitayacd_py/eggs/Plim-0.8.9-py2.7.egg',
'/Volumes/dev/dev/python/pitayacd_py/eggs/stylus-0.1.0-py2.7.egg',
'/Volumes/dev/dev/python/pitayacd_py/eggs/pyScss-1.2.0-py2.7-macosx-10.9-intel.egg',
'/Volumes/dev/dev/python/pitayacd_py/eggs/CoffeeScript-1.0.8-py2.7.egg',
'/Volumes/dev/dev/python/pitayacd_py/eggs/markdown2-2.1.0-py2.7.egg',
'/Volumes/dev/dev/python/pitayacd_py/eggs/Babel-1.3-py2.7.egg',
'/Volumes/dev/dev/python/pitayacd_py/eggs/Mako-0.9.0-py2.7.egg',
'/Volumes/dev/dev/python/pitayacd_py/eggs/PyExecJS-1.0.4-py2.7.egg',
'/Volumes/dev/dev/python/pitayacd_py/eggs/six-1.4.1-py2.7.egg',
'/Volumes/dev/dev/python/pitayacd_py/eggs/pytz-2013.7-py2.7.egg',
'/Volumes/dev/dev/python/pitayacd_py',
]


_interactive = True
if len(sys.argv) > 1:
_options, _args = __import__("getopt").getopt(sys.argv[1:], 'ic:m:')
_interactive = False
for (_opt, _val) in _options:
if _opt == '-i':
_interactive = True
elif _opt == '-c':
exec(_val)
elif _opt == '-m':
sys.argv[1:] = _args
_args = []
__import__("runpy").run_module(
_val, {}, "__main__", alter_sys=True)

if _args:
sys.argv[:] = _args
__file__ = _args[0]
del _options, _args
__file__f = open(__file__)
exec(compile(__file__f.read(), __file__, "exec"))
__file__f.close(); del __file__f

if _interactive:
del _interactive
__import__("code").interact(banner="", local=globals())

这样执行程序里用 bin/python run.py 就会使用buildout构建的环境来运行,不需要像virtualenv一样需要先切换到对应的环境再运行。给我的感受是版本管理更方便了,有点像 ruby 的 bundle 的感觉,但没有对应 Gemfiles.lock 的东西。

需要注意的地方

  1. 在 Flask 这类框架中使用autoreload时默认会读取 sys.executable 的值来运行,但实际上 buildout 生成的python解释是只是一个脚本文件,并不是一个真正的解释器,所以 sys.executable 的内容是指上原始的 python 解释器,需要在入口 py 脚本中手动指定。(比如:sys.executable = ‘./bin/python’)
  2. 如果 python 的第三方库没有在 eggs 中指定出来,而是由依赖安装上去的话,那么像 pyScss 之类会在bin目录中生成可执行文件的第三库就不会生成在bin中生成对应的可执行脚本,如果有需要生成的话,就必需在 eggs 中添加。
文章目录
  1. 1. 建立buildout.cfg
  2. 2. buildout.cfg配置
  3. 3. 构建环境
  4. 4. 生成的python解释器
  5. 5. 需要注意的地方