oohcode

$\bigodot\bigodot^H \rightarrow CODE$

UNIX环境高级编程之写在前面

很久没有写读书笔记了,以前的读书笔记都是记在本子上的,或者直接写到书里备注的,但是发现其实接在本子上的都懒得去翻了,写在书里的也不能随身携带,遇到问题如果忘了的话还是要到网上去google,对自己的知识积累很是不利。现在好了,有了github一切都解决了~以前无知,没有认识到github的重要性,现在发现它真是无所不能啊~,希望伟大的墙不要再让码农做翻墙运动了~

《UNIX环境高级编程》(Advanced Programming in the UNIX Environment,后面简称apue)这本书在豆瓣的评分很高。作为一个非计算机出身的人,自知基础薄弱,需要多方面学习,这本经典书让我对linux系统以及进程线程等概念有了更深的认识,希望自己在写读书笔记的过程中能够对其有更加深入的了解,并且学以致用,运用到工作中去~

apue chapter1:UNIX基础知识

本章主要是从程序设计人员角度对UNIX系统的一些术语和概念有一个大概的印象。

UNIX体系结构

首先看下UNIX操作系统的体系及构图:

UNIX操作系统体系结构
从图的内往外看

  1. 第一层是内核(kernel),它是系统软件,控制计算机的硬件资源,提供程序运行环境。
  2. 第二层是系统调用(system call),所有的系统调用都要通过它来实现。

  3. 第三层比较复杂了,主要有库函数(library routines),shell和应用软件(application)。应用软件可以调用库函数,也可以直接调用系统函数,shell则是一种特殊的应用软件。

登录

用过unix系统的都知道,当开机首先会让输入用户名和密码才能进入系统,其实在unix系统开机后首先运行登录程序/bin/login(关于unix从开机到进入系统的过程,先参考阮一峰的这篇博客:linux的启动流程, 稍后做一下补充),然后读取/etc/passwd文件,验证用户名密码是否正确。
这个passwd文件由7个以冒号分割的字段组成,举个例子:
sean:x:504:505::/home/sean:/bin/bash
这七个字段分别是:
登录名:加密口令:数值用户ID:数值组ID:注释字段:起始目录:shell程序
ps:加密口令单数放在一个文件里面,第六章会对其进行介绍

输入和输出

  1. 文件描述符:内核用来标识一个特定进程正在访问的文件的非负整数。
  2. 标准输入、标准输出和标准出错:运行一个新程序shell都会为其打开这三个文件描述符。
  3. 不用缓冲的I/O:程序直接对输入输出进行处理。
  4. 标准I/O:需要先输入输出到缓冲区,然后按照某种策略中缓冲区中输出。

ps:具体的过程后面会讲到。

程序和进程

  • 程序:是存放在磁盘上、处于某个目录中的一个可执行文件。使用6个exec函数中的一个由内核将程序读入存储器,并使其执行。
  • 进程和进程ID:程序的执行实例被称为进程(process)。某些操作系统使用任务(task)表示。UNIX系统确保每个进程都有一个唯一的数字标志符,称为进程ID(process ID)。进程ID总是非负数。

    1
    2
    3
    4
    5
    6
    7
    8
    #include "apue.h"

    int
    main(void)
    {
    printf("hello world from process ID %d\n", getpid());
    exit(0);
    }
    1
    2
    3
    4
    5
    $ ./a.out
    hello world from precess ID 851
    $ ./a.out
    hello world from precess ID 854

  • 进程控制:有三个用于进程控制的主要函数:fork, exec和waitpid。
    • fork:调用fork创建一个新进程。新进程是调用进程的复制品,我们称调用进程为父进程,新创建的进程为子进程。fork向父进程返回新子进程的进程ID,对子进程返回0。因为fork创建一新进程,所以它被调用一次(由父进程),但返回两次(分别是在父进程及子进程中)。
  • 线程和线程ID:为了充分利用对处理器系统的并行性,可以使用多线程(thread)。在一个进程内的所有线程同享统一地址空间、文件描述符、栈以及于进程相关的属性。因为它们能访问统一存储区,所以各线程在访问共享数据时需要采取同步措施以避免不一致性。与进程相同,线程也用ID标识,但是,线程ID只在它所属的进程内起作用。

出错处理

当UNIX函数出错时,常常返回一个负值,而且整型变量errno通常被设置为含有附加信息的一个值。例如open函数如果执行错误则返回-1。在open出错时,有大约15种不同的errno值(文件不存在、权限问题等)。某些函数并不返回负值而是使用另一种约定。
可以将<errno.h>中定义的各种出错分成致命性的和非致命性的两类。对于致命性的错误,无法执行恢复动作,最多只能在用户屏幕上打印出一条出错消息,或者将一条出错消息写如日志文件中,然后终止。对于非致命性的出错,有时可以较妥善地进行处理。大多数非致命性的出错在本质上时暂时的,例如资源短缺,当系统中的活动较少时,这种出错很可能不会发生。

用户标识

  • 用户ID:口令文件登录项中的用户ID(user ID)是一个数值,它向系统标识各个不同的用户。用户ID为0的用户为根(root)超级用户(superuser)
  • 组ID:口令文件登录项也包括用户的组ID(group ID),它是一个数值。组ID也是由系统管理员在指定用户登录名时分配的。下面就是获取用户ID和组ID的例子:
    1
    2
    3
    4
    5
    6
    7
    8
    # include "apue.h"

    int
    main(void)
    {
    printf("uid = %d, gid = %d\n", getuid(), getgid());
    exit(0);
    }
    1
    2
    $ ./a.out
    uid = 205, gid = 105
  • 附加组ID:除了在口令文件中对一个登录名执行一个组ID外,大多数UNIX系统版本还允许一个用户属于另外的组。这就是附加组ID(supplementary group ID)。通过读取/etc/group获得。

信号

信号(signal)是通知进程已经发生某种情况的一种技术。进程如何处理信号由三种选择:

  • 忽略该信号。
  • 按系统默认方式处理。
  • 提供一个函数,信号发生时则调用该函数,这被称为捕捉该信号。使用这种方式,我们要提供自编的函数就将能知道什么时候产生了信号,并按所希望的方式处理它。

很多情况会产生信号,比如终端键盘上有两种产生信号的方法,分别称为中断键(interrupt key, 通常是Delete键或Ctrl+ C)退出键(quit key, 通常是Ctrl+)。另外一种产生信号的方法是调用名为kill的函数,在一个进程中调用该函数就可以向另外一个进程发送一个信号。

时间值

UNIX系统一直使用两种不同的时间值:

  • 日历时间:该值是自1970年1月1日00:00:00以来国际标准时间(UTC)所经过的秒数累计值。系统基本数据类型为time_t用户保存这种时间值。
  • 进程时间:这也被称为CPU时间,用以度量进程使用的中央处理机资源。进程时间以时钟滴答计算,历史上曾经取50、60和100个滴答。系统基本数据类型clock_t用于保存这种时间值。

当度量一个进程的执行时间时,UNIX系统使用三个进程时间值:

  • 时钟时间:又称为墙上时钟时间(wall clock time)。它是进程运行的时间总量,其值与系统中同时运行的进程数有关。
  • 用户CPU时间:是执行用户指令所用的时间。
  • 系统CPU时间:是为该进程执行内核程序所经历的时间。

用户CPU时间 + 系统CPU时间 = CPU时间。
时钟时间 = CPU时间 + 其它等待的时间。

系统调用和库函数

所有的操作系统都提供多种服务的入口点,程序由此向内核请求服务。这些入口点被称为系统调用
UNIX所使用的技术是为每个系统调用在标准C库中设置一个具有同样名字的函数。用户进程用标准C调用序列来调用这些函数,然后,函数又用系统所要求的技术调用相应的内核服务。从应用的角度考虑,可将系统调用视作为C函数。从实现者的角度观察,系统调用和库函数之间有重大区别;但从用户角度看,其区别并不非常重要。系统调用通常提供一种最小接口,而库函数通常提供比较复杂的功能。

图1-2 malloc函数和sbrk系统调用

图1-3 C库函数和系统调用之间的差别

习题

1.3 在1.7节中,perror的参数是用ISO C的属性const定义的,而strerror的整型参数则没有用此属性定义,为什么?

1
2
3
4
5
# include <string.h>
char *strerror(int errnum);

# include <stdio.h>
void perror(const char *msg);

下面先看这两个函数的使用方式:
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
/* perror example */
# include <stdio.h>

int main ()
{
FILE * pFile;
pFile=fopen ("unexist.ent","rb");
if (pFile==NULL)
perror ("The following error occurred");
else
fclose (pFile);
return 0;
}
/*
out put;
The following error occurred: No such file or directory
*/

/* strerror example : error list */
# include <stdio.h>
# include <string.h>
# include <errno.h>

int main ()
{
FILE * pFile;
pFile = fopen ("unexist.ent","r");
if (pFile == NULL)
printf ("Error opening file unexist.ent: %s\n",strerror(errno));
return 0;
}
/*
out put:
Error opening file unexist.ent: No such file or directory
*/

可见perror的参数是程序运行前就定义好的,其输出是先输出msg指向的字符串,然后加上errno值对应的出错信息。
strerror的参数是运行时确定的,是一个errno的值,其输出是errno对应的出错信息.
所以perror的参数带const而strerror的参数不带const

python打包高级进阶:pip and easy_install

如何选择已经安装的不同版本?

其实很简单。以myproject为例:easy_install myproject==version 就可以了,默认是先从本地开始找,如果本地找不到就会从远程源上找。

能不能指定安装路径?

经过再次了解,发现其实是可以的,easy_install -h命令可以看到有以下两条:

1
2
3
--install-dir (-d)             install package to DIR
--script-dir (-s) install scripts to DIR
--prefix installation prefix

第一个其实就是可以指定默认搜索路径的。第二个是可以指定二进制脚本的安装路径,如果指定了第一个而没有指定第二个那就会默认都安装到第一个,第三个是实际包的安装路径,但是只是路径前缀,默认会加上lib/python2.6/site-packages
还有一个问题,如果按照路径不在PYTHONPATH里,那么就会报错,可以通过先把你要制定的路径加入进去:
1
2
3
PYTHONPATH="${PYTHONPATH}:/search/sean/python/packages"
PYTHONPATH="${PYTHONPATH}:/search/sean/lib/python2.6/site-packages"
export PYTHONPATH

然后再次安装:
1
easy_install -d /search/sean/python/packages/ -s /search/sean/python/ --prefix /search/sean  myproject==1.9

可以看到-d需要PYTHONPATH 而-s并不需要,安装完后可以在两个目录中找到安装后的文件
更正:之前说安装多版本的时候会有一个去掉版本的文件夹,其实这个是pip的实现方式,而easy_install的实现方式是:每次安装都会把easy-install.pth路径修改了,python就会去找修改过的路径。
具体pip的版本选择和安装路径的问题时间限制先不发了,研究后再补充~

django+nginx+fastcgi 配置

我们知道刚开始学习django的时候使用的是django内置的服务器,当然这个是为了能够快速的搭建django的运行环境,并不能运用到实际的生产环境中。而django book 这本书中只接受了django+apache和django+lighttpd的配置。但是我们实际的生产环境中nginx使用的很广泛。于是django+nginx+fastcgi的配置方法就有比较了解一下了…

django配置

首先django book这本书中介绍了如何在生产过程中配置setting.py这个文件,其中提到变量DJANGO_SETTINGS_MODULE的值其实就是网站的配置文件的路径,我们可以根据环境的不同选择不同的配置文件,例如开发环境、测试环境和线上环境等。这个变量的设置其实就在网站的根目录下wsgi.py这个文件中。其内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
"""
wsgi.py
"""
import os

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djsite.settings_dev")

# This application object is used by any WSGI server configured to use this
# file. This includes Django's development server, if the WSGI_APPLICATION
# setting points here.
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()

这里设置的默认加载配置文件为:settings_dev.py,这是开发环境的配置文件,用户可以根据需求选择不同的配置文件。

uwsig配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$cat uwsig.xml 
<uwsgi>
<socket>0.0.0.0:3001</socket>
<listen>20</listen>
<master>true</master>
<pidfile>/etc/nginx/uwsgi.pid</pidfile>
<processes>2</processes>
<module>wsgi</module>
<pythonpath>/search/sean/python/djsite/djsite</pythonpath>
<profiler>true</profiler>
<memory-report>true</memory-report>
<enable-threads>true</enable-threads>
<logdate>true</logdate>
<limit_as>6048</limit_as>
</uwsgi>

setuptools配置文件参数详解

前面介绍了python的打包工具setuptools的用法,这里对其配置文件setup.py进行一个详细的介绍,以便以后使用的时候能够找到相应的配置。

常见的参数

这里的参数主要是参考setuptools官方文档而来,下面经常用到的参数进行一个详细的介绍,以后要是有其他的参数,再做补充。
name
这个参数主要是标识工程的名字。这个名字就是打包后的包的名字。
version
这个参数主要是标识工程的版本号,打包后的名字包含这个版本号。
在安装同一个包的不同版本是可以用如下命令:

1
$ pip install -i http://10.11.215.61:3141/simple/ myproject==1.1.7 

packages
这个参数主要是说明要打包的工程的源代码路径,默认不写的话是从本工程的根目录开始,也可自定义,要引入find_packages函数,函数参数是包的相对路径.。
package_dir
这个主要是为了说明包的路径
zip_safe
scripts
entry_points
include_package_data
这个参数是为了表示打包是需要包含的文件,如果值为True,表示setuptools打包时会自动包含package目录下的所有文件,这些文件可能是在版本控制下或者在MANIFEST.in文件中表明的文件。详情请参考文档
注意MANIFEST.ini的写法:
1
2
3
4
include README.rst #包含具体的文件
...
include django/dispatch/license.txt #包含相对目录的文件
recursive-include docs * #循环包含某个文件夹下所有的文件

ps:亲测,版本控制这个好像不太靠谱,还是自己写都包含哪些文件吧~
package_data
跟上面的参数意思差不多,只不过这里表示粒度比较小的文件,上面没有包含到的文件可以在这里包含,一边表示比较具体的文件。
description
long_description
author
author_email
license
keywords
platforms
url

python命令行程序打包

我们知道在linux下通过命令行调用程序其实就是先把程序编译生成二进制文件,然后再把文件放入到PATH中,就可以直接通过文件名进行调用了,这里python程序也不例外。前面讲过了如何将一个python程序打包并发布,这里就讲一下命令行的python打包发布和下载安装过程。

我们还是利用前面博客中的myproject为例,在原来的基础上添加一些代码:

1
2
3
4
5
6
7
8
9
10
11
12
.
├── setup.py
└── src
├── bin
│   ├── command-line
│   └── myproject
└── myproject
├── cmds.py
├── __init__.py
└── __init__.pyc

3 directories, 6 files

可以看出,比myproject一共多了三个文件command-line, myproject, cmds.py三个文件和bin这个文件夹。下面对他们进行详细的介绍:
command-line,mypreject文件
这两个文件的内容很简单
1
2
3
4
5
6
7
$ cat command-line 
#!/usr/bin/env python
import myproject
myproject.test()
$ cat myproject
#!/usr/bin/env python
print 'welcome to command line mod!\n'

可以看出,command-line文件只是调用了myproject的test函数,二myproject只是输出了一个字符串,为了能够执行这两个文件,并且是通过命令行的方式,我们需要在setup.py这个文件中添加下面这个配置:
1
scripts=['src/bin/command-line', 'src/bin/myproject'],

然后安装修改后的源码包:
1
2
3
4
5
$ python setup.py install
...
Installing command-line script to /search/virtual/pypienv/bin
Installing myproject script to /search/virtual/pypienv/bin
...

我们可以看到上面的输出,其含义就是把这两个脚本安装在了bin目录下,由于这个路径是PATH路径,所以可以直接通过命令进行执行了,下面是执行的效果:
1
2
3
4
$ command-line 
Hello World!
$ myproject
welcome to command line mod!

还有一个文件是cmds.py这个文件也是可以通过命令行直接运行的,只不过是通过另外一种方式进行安装的
先看一下cmds.py的内容吧:
1
2
3
4
$ cat cmds.py 
import myproject
def main():
myproject.test()

可以看出,其实这个文件和command-line一样,引用了myproject并输出,唯一不同的是他自己顶一个了一个函数main来调用。
同样setup.py中也要添加一个配置,来找到这个文件并对其进行编译:
1
2
3
entry_points = {
'console_scripts': ['cmds=myproject.cmds:main'],
}

这个配置和前面的不一样,并且里面指定了要执行的脚本的函数名,通过从新安装我们可以看:
1
2
3
4
$ python setup.py install
...
Installing cmds script to /search/virtual/pypienv/bin
...

同样,这个文件编译后也放到在PATH下,通过命令行可以运行:
1
2
$ cmds 
Hello World!

到这里一个通过命令行运行的包就介绍完了.
同样我们可以这个写好的包上传到私有源,然后在其他机器上下载安装,同样可以运行~

python打包

python的打包方式有很多种,而且针对不同的平台又有很多不同的方式,这里指针对linux平台进行介绍……

1 python打包工具选择

关于python打包相关的工具,这篇文档有一个列表,其中打包工具主要分为pip, dislib, setuptools, distribute(已经于setuptools合并)。还有博客也是关于python 打包工具的,这篇博客对于python的各种打包工具,及其优缺点写的很详细,里面说的Distutils2是更为先进的打包工具,但是适用范围还不是太广泛,这里先不做研究了。经过综合对比,目前选中setuptools和pip这两个个比较成熟的打包工具,下面就对他们用法和原理分别进行详细的介绍。

2 setuptools

setuptools的官方文档地址
首先来一个简单的上手过程,通过这个过程可以快速了解setuptools怎么用的,详情请移步博客python egg 学习笔记
看完这个教程就能自己做一个简单的包了,但是这里只是涉及了一种包,就是python import 引过来,然后调用其中的函数,还有一种也很普遍的方式就是命令行方式,例如jekyll命令,sentry命令等,这种方式其实可以参考这篇文档。跟随这两篇博客基本就可以满足我们的需求了
这里对其安装步骤进行一下总结:

2.1, 安装setuptools

1
2
$ wget http://peak.telecommunity.com/dist/ez_setup.py
$ sudo python ez_setup.py

其实安装的方式有很多种,具体方法参见前面提到的博客

2.2, 建立一个工程

1
2
3
$ mkdir mypackage 
$ cd mypackage
$ touch setup.py

setup.py的内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 1 #setup.py
2 #!/usr/bin/env python
3 #-*- coding:utf-8 -*-
4
5 from setuptools import setup, find_packages
6
7 setup(
8 name = "myproject",
9 version="0.1.0",
10 packages = find_packages(),
11 zip_safe = False,
12
13 description = "egg test demo.",
14 long_description = "egg test demo, haha.",
15 author = "sean",
16 author_email = "seanerchen@gmail.com",
17
18 license = "GPL",
19 keywords = ("test", "egg"),
20 platforms = "Independant",
21 url = "",
22 )

第8行表示这个工程的名称,第9行是这个工程的版本号,第10行表示对指定目录的文件打包,其他信息请查看相关文档,这里先对用到的配置项进行讲解

2.3, 通过命令行打包到本地

这是一个空的工程就建好了,我们可以通过一条简单的命令对它进行打包了:

1
2
python setup.py bdist_egg 
#python setup.py bdist 命令也可以。它们之间的区别?

命令执行后,myproject目录的的结构如下:
1
2
3
4
5
6
7
8
9
10
11
12
.
├── build
│   └── bdist.linux-x86_64
├── dist
│   └── myproject-0.1.0-py2.6.egg
├── myproject.egg-info
│   ├── dependency_links.txt
│   ├── not-zip-safe
│   ├── PKG-INFO
│   ├── SOURCES.txt
│   └── top_level.txt
└── setup.py

我们可以看到在dist文件夹下生成了一个egg文件,通过使用命令file可以查看这个文件的格式:
1
2
$ file dist/myproject-0.1.0-py2.6.egg
dist/myproject-0.1.0-py2.6.egg: Zip archive data, at least v2.0 to extract

其实egg文件就一个压缩包,关于egg的介绍请参考这篇博客

2.4, 写自己的程序并打包到本地

在当前目录下建立一个文件夹,名字与setup.py里写的name字段保持一致,然后在进入目录,创建init.py文件

1
2
3
$ mkdir myproject
$ cd myproject
$ touch __init__.py

文件__init__.py内容如下:
1
2
3
4
5
6
7
1 #!/usr/bin/env python
2 #-*-coding:utf-8-*-
3
4 def test():
5 print "Hello World!"
6 if __name__ == '__main__':
7 test()

再次运行生成egg的命令:

1
python setup.py bdist_egg 

这时整个目录结构变成了下面的情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.
├── build
│   ├── bdist.linux-x86_64
│   └── lib
│   └── myproject
│   └── __init__.py
├── dist
│   └── myproject-0.1.0-py2.6.egg
├── myproject
│   └── __init__.py
├── myproject.egg-info
│   ├── dependency_links.txt
│   ├── not-zip-safe
│   ├── PKG-INFO
│   ├── SOURCES.txt
│   └── top_level.txt
└── setup.py

可以看一下这个egg文件的内容:
1
2
3
4
5
6
7
8
9
10
11
12
13
$ unzip -l dist/myproject-0.1.0-py2.6.egg 
Archive: dist/myproject-0.1.0-py2.6.egg
Length Date Time Name
--------- ---------- ----- ----
118 11-01-2013 14:35 myproject/__init__.py
354 11-01-2013 14:35 myproject/__init__.pyc
232 11-01-2013 14:35 EGG-INFO/PKG-INFO
194 11-01-2013 14:35 EGG-INFO/SOURCES.txt
1 11-01-2013 14:35 EGG-INFO/dependency_links.txt
1 10-21-2013 12:29 EGG-INFO/not-zip-safe
10 11-01-2013 14:35 EGG-INFO/top_level.txt
--------- -------
910 7 files

可以看到里面多出来一个myproject文件夹和它所包含的文件,正是我们刚才创建的。

2.5, 打包到远程

这里我们只是把打的包放在了本地, 也可以根据前面讲的打包到远程的源中,这里结合前面的博客把这个包发送到私有源,命令如下:

1
2
3
4
5
6
7
8
$ python setup.py sdist upload -r local 
running sdist
running egg_info
...
removing 'myproject-0.1.0' (and everything under it)
running upload
Submitting dist/myproject-0.1.0.tar.gz to http://127.0.0.1:3141
Server response (200): OK

这样我们就可以把本地的包上传到了远程的私有源local上供其他人使用了。

2.6, 安装和使用

安装可以分为本地安装和远程安装。这个和之前私有源建立的博客中讲的一样,具体方式如下:
1,本地安装:

1
2
3
4
5
6
7
8
9
10
11
$ sudo python setup.py install
running install
running bdist_egg
running egg_info
...
...
Installed /root/.local/lib/python2.6/site-packages/myproject-0.1.0-py2.6.egg
Processing dependencies for myproject==0.1.0
Finished processing dependencies for myproject==0.1.0
$ python -c "from myproject import test; test()"
Hello World!

可以看到我们的包已经被安装了,并且可以运行我们之前在包里封装的方法。
2,远程安装:
我们可以安装远程私有源的包到本地:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 安装远程包到本地
$ easy_install -i http://10.11.215.61:3141/simple/ myproject
Searching for myproject
Reading http://10.11.215.61:3141/simple/myproject/
Best match: myproject 0.1.0
Downloading http://10.11.215.61:3141/packages/myproject-0.1.0.tar.gz
Processing myproject-0.1.0.tar.gz
Writing /tmp/easy_install-szH61Z/myproject-0.1.0/setup.cfg
Running myproject-0.1.0/setup.py -q bdist_egg --dist-dir /tmp/easy_install-szH61Z/myproject-0.1.0/egg-dist-tmp-aI8vPL
Adding myproject 0.1.0 to easy-install.pth file

Installed /usr/lib/python2.6/site-packages/myproject-0.1.0-py2.6.egg
Processing dependencies for myproject
Finished processing dependencies for myproject

这时刚才上传到私有源的包就安装在了本地,安装路径可以从安装的信息中看到。安装的路径为:/usr/lib/python2.6/site-packages/myproject-0.1.0-py2.6.egg

2.7, 代码结构规划

一般情况下为了使代码看起来更加有条理,我们把源代码放到src文件夹下。首先把myproject文件夹移动到src中,然后删除以前产生的文件,只保留自己写的问文件,其结构大致如下:

1
2
3
4
5
   ├── setup.py
└── src
└── myproject
├── __init__.py
└── __init__.pyc

setup.py内容也要修改,修改默认的搜索路径为src,而不是从根目录开始,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# setup.py
# !/usr/bin/env python
# -*- coding:utf-8 -*-

from setuptools import setup, find_packages

setup(
name = "myproject",
version="0.1.0",
packages = find_packages('src'),
package_dir = {'':'src'},
zip_safe = False,

description = "egg test demo.",
long_description = "egg test demo, haha.",
author = "sean",
author_email = "seanerchen@gmail.com",

license = "GPL",
keywords = ("test", "egg"),
platforms = "Independant",
url = "",
)

然后运行命令生成egg等文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ python setup.py bdist_egg

$ tree
.
├── build
│   ├── bdist.linux-x86_64
│   └── lib
│   └── myproject
│   └── __init__.py
├── dist
│   └── myproject-0.1.0-py2.6.egg
├── setup.py
└── src
├── myproject
│   ├── __init__.py
│   └── __init__.pyc
└── myproject.egg-info
├── dependency_links.txt
├── not-zip-safe
├── PKG-INFO
├── SOURCES.txt
└── top_level.txt

8 directories, 10 files

然后用同样的方式进行安装运行,依然没有问题。

2.8, 卸载程序

安装过的python包,如果要删除该如何做呢?
首先要找到easy-install.pth文件,这个文件是自动查询安装包的路径的文件,如果把安装包的路径从这个文件中去掉,将找不到要使用的包
当运行安装程序发现最后几行信息:

1
2
3
4
5
6
7
8
9
10
$ python setup.py install
...
Processing myproject-0.1.0-py2.6.egg
creating /root/.local/lib/python2.6/site-packages/myproject-0.1.0-py2.6.egg
Extracting myproject-0.1.0-py2.6.egg to /root/.local/lib/python2.6/site-packages
Adding myproject 0.1.0 to easy-install.pth file

Installed /root/.local/lib/python2.6/site-packages/myproject-0.1.0-py2.6.egg
Processing dependencies for myproject==0.1.0
Finished processing dependencies for myproject==0.1.0

可以看出包的安装路径和包的路径信息都能找到,删除相应的包和路径就会发现无法使用该包了。
打开文件easy-install.pth,删除./myproject-0.1.0-py2.6.egg这一行,或者直接删除安装好的egg包,再次运行python时就会提示找不到该包
1
2
3
4
>>> from myproject import test
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: No module named myproject

到这里整个配置文件的写法,源代码的写法,打包到本地和打包到远程,本地安装和远程安装都介绍完了。

python pypi 私有源搭建

今天学习了pypi的私有源的搭建。过程很曲折,出现了很多问题,这里做个记录,希望以后的人能有所借鉴,自己也做一下总结……

什么是pypi

这个问题可以从官方文档得到答案。pypi的全称是:The Python Package Index,翻译过来就是一个python包的索引,python包存储在一个统一管理的仓库中,然后为所有在这个仓库的软件生成一个列表,这就是pypi.

如何使用pypi

用过python的同学都知道,通过下面三个命令都可以安装python包。但是它们之间的区别又是什么呢?

1
1. python setup.py  install

1
2. pip install packagename

1
3. easy_install packagename

第一种方式就是先把要安装的包的模块下载到本地,然后进入解压目录,执行上面的命令。
第二种和第三种都是直接输入命令,就会自动从pipy库中下载相应的包然后安装的预先定义好的路径。
如果不是要修改源码,第二种和第三种方式无疑是最好的。这里先不深入他们的原理,以后再讲。

为什么要搭建pypi私有源

既然有了官方的pypi源,为什么我们还要搭建自己的私有源呢?这个问题其实很好理解。公司内部开发的软件包希望能够通过这种方式让大家安装,但如果上传到官方的源无疑是公司所不希望的,这时私有源就发挥作用了。它只在公司内部服务器上存在,安装方式和官方的源一样,既简单有安全,何乐而不为呢~

如何搭建自己的私有源

终于到了正题了,如何搭建我们自己的私有源呢?这里我主要参考了易先生的世界这篇博客。
从中我们可以得知python的私有源解决方案有很多种,这些解决方案可以在python关于私有源的官方文档中找到。这里我们选择了据说是最简单好用的一个pypiserver,下面就这个私有源的安装过程进行一个说明(ps:我的软件都是用virtualenv方式安装的):
顺利的安装方式

1
2
3
4
5
6
7
8
9
10
# 进入virtualenv目录。执行:
virtualenv pypienv
# 然后进入pypienv环境
source pypienv/bin/activate
# 安装pypiserver
pip install pypiserver
# 建立一个存放软件包的目录,这个目录路径可按照自己的需求建立
mkdir /PATH/packages
# 启动pypi-server 端口号自定义 后接包的上传地址
exec pypi-server -p 3141 /search/ziyuan/pypiserver/packages

到这里其实pypi-server的安装已经完成了。但是我们安装好了要用,这才是重点。
上传package
假设我们已经做好了一个package,如何上传到指定的私有源呢?
首先上传需要用户名和密码,这个需要在服务端生成,密码文件使用命令htpasswd生成。首先安装passlib模块
1
pip install passlib

然后安装apache2-utils模块。由于我使用的readhat,并没有在yum中找到apache2-utils源,只有自己下载安装了。下载地址
1
rpm -ivh apache2-2.4.6-7.3.src.rpm

最后生成用户名和密码:
1
htpasswd -sc /PATH/.htaccess user #回车输入密码。输入:123

用户名密码都生成了,接下来就是从新启动pypiserver,让他读入设置好的用户名密码
1
exec pypi-server -p 3141 -P /PATH/.htaccess /search/ziyuan/pypiserver/packages

用户上传软件包之前要指定上传的的地址,否则就会传到官方的默认路径去,执行路径的配置文件要放在用户主目录下,文件名.pypirc,内容如下:
1
2
3
4
5
6
7
8
[distutils]
index-servers =
local #注意:这个缩进必须要有,不然就会报错,解析不了配置文件

[local]
repository:http://127.0.0.1:3141
username:user
password:123

这是就可以在和发布的软件包中域名命令上传软件包到私有源了:
1
python setup.py sdist upload -r local 

下载package
前面也提到了有两种下载方式可以直接从源中下载,根据打包的方式不同。它们分别是:
1
2
pip install -i http://localhost:3141/simple/ packagename
easy_install -i http://localhost:3141/simple/ packagename

到这里,pypiserver的安装和使用已经完成了,但是其原理还需要研究,以后要做到定制一些功能~

study liquid

这篇博客主要讲学习liquid这个语言的语法

下面还会讲学习markdown的语法

1
2
3
4
5
6
<?php
$arr = (1,3,4);
$var = 'testfasdfasdfasdfasdfasdfasdfasdasdfasdfasdasdfasdfasfasdfasdfasdfasfasfadfsdfasdfasdfasdfasdfasdfasdfasdfasdaff';
echo $var;
print_r($arr);
?>

安装新主题

安装完jekyll后很是兴奋,对于一向是外貌协会的我自然希望能找一个好的博客主题,这个主题一定要高端大气上档次,并且体现黑客的风格,这样才好装B啊,于是一天的时间都花在这上面了~

心血来潮

对于一个新手来说,安装好jekyll后,我第一件事就是希望能够找到一个漂亮的主题,切合我博客的内容,于是我开始了忙碌的寻找……
在学习jekyll boostrapy的过程中看到了可以自定义主题,并且通过简单的命令就可以安装,实在是太爽了,于是我毫不犹豫的操作了起来
添加远程主题

1
rake theme:install git="https://github.com/jekyllbootstrap/twitter.git"

安装本地主题
1
rake theme:package title="twitter"

切换主题:
1
rake theme:switch title="twitter"

到这里其实可以结束了,但是总是感觉自带的主题和我自己的审美有冲突

痛苦挣扎

失败告终

总结