Python 环境管理与进程处理全解析
在 Python 开发中,环境管理和进程处理是非常重要的部分。本文将详细介绍虚拟环境管理工具 virtualenv 和 EPM 包管理器,以及 Python 中的 subprocess 模块在进程处理方面的应用。
1. virtualenv:创建隔离的 Python 环境
virtualenv 是一个强大的工具,用于创建独立的 Python 环境,每个环境都可以有自己的 Python 版本和安装的包,避免了不同项目之间的依赖冲突。
1.1 创建不同 Python 版本的虚拟环境
可以使用 virtualenv 为不同的 Python 版本创建独立的虚拟环境。例如,创建 Python 2.4 和 Python 2.5 的虚拟环境:
$ virtualenv-py24 /tmp/sandbox/py24ENV
New python executable in /tmp/sandbox/py24ENV/bin/python
Installing setuptools.................done.
$ virtualenv-py25 /tmp/sandbox/py25ENV
New python executable in /tmp/sandbox/py25ENV/bin/python
Installing setuptools..........................done.
创建完成后,可以查看虚拟环境的目录结构:
$ ls /tmp/sandbox/py24ENV/
bin/ lib/
$ ls /tmp/sandbox/py24ENV/bin/
activate easy_install* easy_install-2.4* python* python2.4@
从输出可以看出,virtualenv 创建了相对的
bin
目录和
lib
目录,
bin
目录中包含一个 Python 解释器,它使用
lib
目录作为自己的本地
site-packages
目录。此外,还预填充了
easy_install
脚本,方便在虚拟环境中安装包。
1.2 使用虚拟环境的两种方式
使用虚拟环境有两种方式:
-
显式调用完整路径
:可以直接使用虚拟环境的完整路径来调用 Python 解释器,例如:
$ /src/virtualenv-py24/bin/python2.4
-
使用激活脚本
:可以使用虚拟环境
bin目录中的activate脚本来设置环境,无需输入完整路径。例如:
$ source /tmp/sandbox/py24ENV/bin/activate
激活后,在当前终端中执行的 Python 命令将使用该虚拟环境。如果需要退出虚拟环境,可以执行
deactivate
命令。
1.3 创建自定义的引导虚拟环境
从 virtualenv 1.0 版本开始,支持创建用于虚拟环境的引导脚本。可以使用
virtualenv.create_bootstrap_script(text)
方法创建一个引导脚本,该脚本类似于 virtualenv,但具有扩展选项解析、调整选项和使用
after_install
钩子的额外功能。
以下是一个创建自定义引导脚本的示例,该脚本将安装
liten
包到新环境中:
import virtualenv, textwrap
output = virtualenv.create_bootstrap_script(textwrap.dedent("""
import os, subprocess
def after_install(options, home_dir):
etc = join(home_dir, 'etc')
if not os.path.exists(etc):
os.makedirs(etc)
subprocess.call([join(home_dir, 'bin', 'easy_install'),
'liten'])
"""))
f = open('liten-bootstrap.py', 'w').write(output)
运行该脚本后,会生成一个
liten-bootstrap.py
文件。如果不带任何选项运行该文件,会显示使用说明:
$ python liten-bootstrap.py
You must provide a DEST_DIR
Usage: liten-bootstrap.py [OPTIONS] DEST_DIR
Options:
--version show program's version number and exit
-h, --help show this help message and exit
-v, --verbose Increase verbosity
-q, --quiet Decrease verbosity
--clear Clear out the non-root install and start from scratch
--no-site-packages Don't give access to the global site-packages dir to the
virtual environment
当指定目标目录运行时,会自动创建一个包含
liten
包的虚拟环境:
$ python liten-bootstrap.py --no-site-packages /tmp/liten-ENV
New python executable in /tmp/liten-ENV/bin/python
Installing setuptools..........................done.
Searching for liten
Best match: liten 0.1.3
Processing liten-0.1.3-py2.5.egg
Adding liten 0.1.3 to easy-install.pth file
Installing liten script to /tmp/liten-ENV/bin
Using /Library/Python/2.5/site-packages/liten-0.1.3-py2.5.egg
Processing dependencies for liten
Finished processing dependencies for liten
运行
liten
工具:
$ /tmp/liten-ENV/bin/liten
Usage: liten [starting directory] [options]
A command-line tool for detecting duplicates using md5 checksums.
Options:
--version show program's version number and exit
-h, --help show this help message and exit
-c, --config Path to read in config file
-s SIZE, --size=SIZE File Size Example: 10bytes, 10KB, 10MB,10GB,10TB, or
plain number defaults to MB (1 = 1MB)
-q, --quiet Suppresses all STDOUT.
-r REPORT, --report=REPORT
Path to store duplication report. Default CWD
-t, --test Runs doctest.
2. EPM 包管理器:创建跨平台软件包
EPM(Easy Package Manager)是一个用于创建跨平台软件包的工具,它可以为不同的操作系统创建原生软件包。
2.1 EPM 包管理器的安装
EPM 只需要 Bourne 类型的 shell、C 编译器、
make
程序和
gzip
这些工具。在大多数 *nix 系统上,如果这些工具没有安装,可以很容易地获取。下载 EPM 的源代码后,需要运行以下命令进行安装:
./configure
make
make install
2.2 创建要分发的命令行工具
为了演示 EPM 的使用,我们创建一个简单的命令行工具
hello_epm.py
:
#!/usr/bin/env python
import optparse
def main():
p = optparse.OptionParser()
p.add_option('--os', '-o', default="*NIX")
options, arguments = p.parse_args()
print 'Hello EPM, I like to make packages on %s' % options.os
if __name__ == '__main__':
main()
运行该工具:
$ python hello_epm.py
Hello EPM, I like to make packages on *NIX
$ python hello_epm.py --os RedHat
Hello EPM, I like to make packages on RedHat
2.3 创建平台特定的软件包
EPM 通过读取 “list” 文件来描述软件包。以下是一个用于创建
hello_epm
工具软件包的 “list” 文件示例:
#EPM List File Can Be Used To Create Package For Any Of These Vendor Platforms
#epm -f format foo bar.list ENTER
#The format option can be one of the following keywords:
#aix - AIX software packages.
#bsd - FreeBSD, NetBSD, or OpenBSD software packages.
#depot or swinstall - HP-UX software packages.
#dpkg - Debian software packages.
#inst or tardist - IRIX software packages.
#native - "Native" software packages (RPM, INST, DEPOT, PKG, etc.) for the platform.
#osx - MacOS X software packages.
#pkg - Solaris software packages.
#portable - Portable software packages (default).
#rpm - Red Hat software packages.
#setld - Tru64 (setld) software packages.
#slackware - Slackware software packages.
# Product Information Section
%product hello_epm
%copyright 2008 Py4SA
%vendor O’Reilly
%license COPYING
%readme README
%description Command Line Hello World Tool
%version 0.1
# Autoconfiguration Variables
$prefix=/usr
$exec_prefix=/usr
$bindir=${exec_prefix}/bin
$datadir=/usr/share
$docdir=${datadir}/doc/
$libdir=/usr/lib
$mandir=/usr/share/man
$srcdir=.
# Executables
%system all
f 0555 root sys ${bindir}/hello_epm hello_epm.py
# Documentation
%subpackage documentation
f 0444 root sys ${docdir}/README $srcdir/README
f 0444 root sys ${docdir}/COPYING $srcdir/COPYING
f 0444 root sys ${docdir}/hello_epm.html $srcdir/doc/hello_epm.html
# Man pages
%subpackage man
%description Man pages for hello_epm
f 0444 root sys ${mandir}/man1/hello_epm.1 $srcdir/doc/hello_epm.man
为了创建软件包,需要在当前工作目录中创建相应的文件,如
README
、
COPYING
、
doc/hello_epm.html
和
doc/hello_epm.man
,并将
hello_epm.py
脚本放在同一目录下。
以下是创建这些文件的示例:
$ pwd
/tmp/release/hello_epm
$ touch README
$ touch COPYING
$ mkdir doc
$ touch doc/hello_epm.html
$ touch doc/hello_epm.man
查看目录结构:
$ ls -lR
total 16
-rw-r--r-- 1 ngift wheel 0 Mar 10 04:45 COPYING
-rw-r--r-- 1 ngift wheel 0 Mar 10 04:45 README
drwxr-xr-x 4 ngift wheel 136 Mar 10 04:45 doc
-rw-r--r-- 1 ngift wheel 1495 Mar 10 04:44 hello_epm.list
-rw-r--r--@ 1 ngift wheel 278 Mar 10 04:10 hello_epm.py
./doc:
total 0
-rw-r--r-- 1 ngift wheel 0 Mar 10 04:45 hello_epm.html
-rw-r--r-- 1 ngift wheel 0 Mar 10 04:45 hello_epm.man
2.4 制作软件包
在有了包含通用指令的 “list” 文件后,只需运行
epm -f
命令并指定平台和 “list” 文件的名称即可创建软件包。以下是在 OS X 上创建原生安装程序的示例:
$ epm -f osx hello_epm hello_epm.list
epm: Product names should only contain letters and numbers!
^C
$ epm -f osx helloEPM hello_epm.list
$ ll
total 16
-rw-r--r-- 1 ngift wheel 0 Mar 10 04:45 COPYING
-rw-r--r-- 1 ngift wheel 0 Mar 10 04:45 README
drwxr-xr-x 4 ngift wheel 136 Mar 10 04:45 doc
-rw-r--r-- 1 ngift wheel 1495 Mar 10 04:44 hello_epm.list
-rw-r--r--@ 1 ngift wheel 278 Mar 10 04:10 hello_epm.py
drwxrwxrwx 6 ngift staff 204 Mar 10 04:52 macosx-10.5-intel
注意,当包名包含下划线时会出现警告,因此将包名重命名后再次运行。创建完成后,会生成一个
macosx-10.5-intel
目录,其中包含
.dmg
镜像存档和原生 OS X 安装程序:
$ ls -la macosx-10.5-intel
total 56
drwxrwxrwx 4 ngift staff 136 Mar 10 04:54 .
drwxr-xr-x 8 ngift wheel 272 Mar 10 04:54 ..
-rw-r--r--@ 1 ngift staff 23329 Mar 10 04:54 helloEPM-0.1-macosx-10.5-intel.dmg
drwxr-xr-x 3 ngift wheel 102 Mar 10 04:54 helloEPM.mpkg
运行安装程序后,OS X 会安装空白的手册页和文档,并显示空白的许可证文件,最后将工具安装到指定的位置:
$ which hello_epm
/usr/bin/hello_epm
$ hello_epm
Hello EPM, I like to make packages on *NIX
$ hello_epm -h
Usage: hello_epm [options]
Options:
-h, --help show this help message and exit
-o OS, --os=OS
3. Subprocess 模块:Python 中的进程处理
在 Python 中,
subprocess
模块是处理进程的重要工具,它从 Python 2.4 版本开始引入,取代了
os.system
、
os.spawn
、
os.popen
和
popen2
等旧模块。
subprocess
模块提供了一个统一的 API 来执行外部命令,与标准输入、标准输出和标准错误进行交互,并监听返回码。
3.1 简单的系统调用
使用
subprocess.call()
函数可以执行简单的系统调用。以下是一个查看磁盘使用情况的示例:
import subprocess
subprocess.call('df -k', shell=True)
输出结果:
Filesystem 1024-blocks Used Available Capacity Mounted on
/dev/disk0s2 97349872 80043824 17050048 83% /
devfs 106 106 0 100% /dev
fdesc 1 1 0 100% /dev
map -hosts 0 0 0 100% /net
map auto_home 0 0 0 100% /home
3.2 包含 shell 变量的系统调用
可以在系统调用中包含 shell 变量。以下是一个查看主目录磁盘使用情况的示例:
import subprocess
subprocess.call('du -hs $HOME', shell=True)
输出结果:
28G /Users/ngift
3.3 抑制标准输出
有时候,只需要运行系统调用而不关心标准输出,可以使用
stdout=open('/dev/null', 'w')
来抑制标准输出。以下是一个 ping 命令的示例:
import subprocess
ret = subprocess.call("ping -c 1 10.0.1.1",
shell=True,
stdout=open('/dev/null', 'w'),
stderr=subprocess.STDOUT)
3.4 使用返回码判断命令执行结果
每个进程退出时都会有一个返回码,可以使用返回码来判断命令的执行结果。一般来说,返回码为 0 表示成功,非 0 表示失败。以下是一些常见的返回码及其含义:
| 返回码 | 含义 |
| ---- | ---- |
| 0 | 成功 |
| 1 | 一般错误 |
| 2 | 误用 shell 内置命令 |
| 126 | 调用的命令无法执行 |
| 127 | 命令未找到 |
| 128 | 无效的退出参数 |
| 130 | 脚本被 Ctrl-C 终止 |
| 255 | 退出状态超出范围 |
以下是使用返回码进行条件判断的示例:
import subprocess
ret = subprocess.call("ls /foo", shell=True)
if ret == 0:
print "success"
else:
print "failure"
3.5 捕获标准输出
如果需要捕获命令的输出,可以使用
subprocess.Popen()
函数。以下是一个查看磁盘使用情况并捕获输出的示例:
import subprocess
p = subprocess.Popen("df -h", shell=True, stdout=subprocess.PIPE)
out = p.stdout.readlines()
for line in out:
print line.strip()
3.6 与标准输入进行交互
subprocess
模块还可以与标准输入进行交互。以下是一个向
wc -c
命令传递字符串并统计字符数的示例:
import subprocess
p = subprocess.Popen("wc -c", shell=True, stdin=subprocess.PIPE)
p.communicate("charactersinword")
输出结果:
16
3.7 命令管道
在 Python 中可以使用
subprocess
模块实现命令管道。以下是一个在 Bash 和 Python 中实现相同命令管道的示例:
-
Bash 示例
:
cat /etc/passwd | grep 0:0 | cut -d ':' -f 7
- Python 示例 :
import subprocess
p1 = subprocess.Popen("cat /etc/passwd", shell=True, stdout=subprocess.PIPE)
p2 = subprocess.Popen("grep 0:0", shell=True, stdin=p1.stdout, stdout=subprocess.PIPE)
p3 = subprocess.Popen("cut -d ': ' -f 7", shell=True, stdin=p2.stdout, stdout=subprocess.PIPE)
print p3.stdout.read()
4. 总结
通过本文的介绍,我们了解了 virtualenv 如何创建隔离的 Python 环境,EPM 包管理器如何创建跨平台的软件包,以及
subprocess
模块在 Python 中处理进程的强大功能。这些工具和模块可以帮助开发者更好地管理项目环境和处理系统进程,提高开发效率。
Python 环境管理与进程处理全解析
5. 基于 Subprocess 模块的扩展应用
5.1 创建命令工厂函数
在前面提到可以使用
subprocess.Popen
来创建一个命令工厂函数,以任意运行多个命令。这个函数可以接收多个命令作为参数,并依次执行这些命令。以下是具体的代码实现:
import subprocess
def multi(*args):
for cmd in args:
p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
out = p.stdout.read()
print(out)
使用示例:
multi("df -h", "ls -l /tmp", "tail /var/log/system.log")
这个函数的工作流程如下:
1. 接收任意数量的命令作为参数。
2. 遍历这些命令,对于每个命令,使用
subprocess.Popen
打开一个新的进程。
3. 读取该进程的标准输出,并将其打印出来。
流程图如下:
graph TD;
A[开始] --> B[接收命令列表];
B --> C[遍历命令列表];
C --> D[打开新进程执行命令];
D --> E[读取标准输出];
E --> F[打印输出];
F --> C;
C --> G[结束];
5.2 创建 Subprocess 模块的封装类
为了进一步简化和自动化
subprocess
的使用,可以创建一个模块来封装相关功能。下面是一个示例代码:
from subprocess import call
import time
import sys
"""Subtube is module that simplifies and automates some aspects of subprocess"""
class BaseArgs(object):
"""Base Argument Class that handles keyword argument parsing"""
def __init__(self, *args, **kwargs):
self.args = args
self.kwargs = kwargs
if self.kwargs.has_key("delay"):
self.delay = self.kwargs["delay"]
else:
self.delay = 0
if self.kwargs.has_key("verbose"):
self.verbose = self.kwargs["verbose"]
else:
self.verbose = False
def run(self):
"""You must implement a run method"""
raise NotImplementedError
class Runner(BaseArgs):
"""Simplifies subprocess call and runs call over a sequence of commands
Runner takes N positional arguments, and optionally:
[optional keyword parameters]
delay=1, for time delay in seconds
verbose=True for verbose output
Usage:
cmd = Runner("ls -l", "df -h", verbose=True, delay=3)
cmd.run()
"""
def run(self):
for cmd in self.args:
if self.verbose:
print("Running %s with delay=%s" % (cmd, self.delay))
time.sleep(self.delay)
call(cmd, shell=True)
使用示例:
from subtube import Runner
r = Runner("df -h", "du -h /tmp")
r.run()
这个封装类的使用步骤如下:
1. 定义
BaseArgs
类,用于处理关键字参数的解析,包括
delay
和
verbose
参数。
2. 定义
Runner
类,继承自
BaseArgs
类,实现
run
方法,用于依次执行传入的命令。
3. 在
run
方法中,如果
verbose
为
True
,则打印正在执行的命令和延迟时间;然后根据
delay
参数进行延迟;最后使用
subprocess.call
执行命令。
5.3 远程命令执行示例
如果在多个系统上设置了 SSH 密钥,就可以使用上述封装类来实现远程命令执行。以下是一个简单的示例:
machines = ['homer', 'marge', 'lisa', 'bart']
for machine in machines:
r = Runner("ssh " + machine + " df -h", "ssh " + machine + " du -h /tmp")
r.run()
这个示例的工作流程如下:
1. 定义一个包含多个主机名的列表。
2. 遍历这个列表,对于每个主机,创建一个
Runner
对象,并传入要在该主机上执行的命令。
3. 调用
Runner
对象的
run
方法,依次执行这些命令。
6. 跨平台开发中的注意事项
在使用
subprocess
进行跨平台开发时,需要注意不同操作系统的差异,特别是返回码的不同。不同操作系统对于相同的错误情况可能会返回不同的返回码。
6.1 返回码的差异
例如,在 Red Hat 系统上,
rsync
命令未找到时返回码为 127,但在 Solaris 10 系统上返回码为 1。因此,在编写跨平台代码时,不能仅仅依赖于特定的返回码,而应该先确定操作系统,再根据操作系统来检查命令的返回码。
以下是一个使用
platform
模块来确定操作系统,并根据操作系统执行不同命令的示例:
import platform
import subprocess
if platform.system() == 'SunOS':
ret = subprocess.call('cp /tmp/foo.txt /tmp/bar.txt', shell=True)
if ret == 0:
print("Success, the copy was made on %s %s " % (platform.system(), platform.release()))
这个示例的步骤如下:
1. 导入
platform
和
subprocess
模块。
2. 使用
platform.system()
函数获取当前操作系统的名称。
3. 如果操作系统是
SunOS
,则执行
cp
命令,并检查返回码。
4. 如果返回码为 0,表示命令执行成功,打印相应的信息。
6.2 跨平台代码的编写建议
-
使用
platform模块 :在代码中使用platform模块来确定当前操作系统,根据不同的操作系统执行不同的操作。 - 避免依赖特定的返回码 :尽量只依赖于通用的返回码,如 0 表示成功,非 0 表示失败。
- 测试不同操作系统 :在不同的操作系统上进行充分的测试,确保代码在各种环境下都能正常工作。
7. 总结与展望
通过本文的详细介绍,我们全面了解了 Python 中环境管理和进程处理的重要工具和技术。
virtualenv
为我们提供了创建隔离 Python 环境的能力,使得不同项目之间的依赖管理更加方便;EPM 包管理器则让我们能够轻松创建跨平台的软件包,提高了软件的分发效率;
subprocess
模块作为 Python 中处理进程的核心工具,提供了强大的功能,包括执行系统调用、与标准输入输出交互、处理返回码等。
在未来的开发中,我们可以进一步探索这些工具和模块的高级用法。例如,对于
virtualenv
,可以研究如何更好地管理多个虚拟环境,实现自动化的环境切换;对于 EPM 包管理器,可以深入了解其处理依赖关系、运行预安装和后安装脚本的功能;对于
subprocess
模块,可以开发更复杂的封装类,以满足更多的实际需求。同时,随着 Python 版本的不断更新,这些工具和模块也可能会有新的特性和改进,我们需要持续关注并学习,以跟上技术的发展步伐。
总之,掌握这些工具和技术将有助于我们更加高效地进行 Python 开发,提高代码的质量和可维护性。希望本文能够为读者在 Python 环境管理和进程处理方面提供有益的参考和指导。
超级会员免费看
1万+

被折叠的 条评论
为什么被折叠?



