28、Python 环境管理与进程处理全解析

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 环境管理和进程处理方面提供有益的参考和指导。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值