<h2>我的Python开发工作流</h2>
<p>这是开发Python包的一个基本过程,它包含了我认为是社区中最佳实践的东西。这是基本的-如果你真的很认真地开发Python包,还有更多的东西要做,每个人都有自己的偏好,但它应该作为一个模板,开始学习,然后了解更多相关的部分。基本步骤包括:</p>
<ul>
<li>使用<a href="https://pypi.python.org/pypi/virtualenv" rel="noreferrer">^{<cd1>}</a>隔离</li>
<li><a href="https://pypi.org/project/setuptools/" rel="noreferrer">^{<cd2>}</a>用于创建可安装包并管理依赖项</li>
<li><code>python setup.py develop</code>以开发模式安装该包</li>
</ul>
<h2>虚拟人</h2>
<p>首先,我建议使用<a href="https://pypi.python.org/pypi/virtualenv" rel="noreferrer">^{<cd1>}</a>来获得一个独立的环境来开发您的包。在开发过程中,您将需要安装、升级、降级和卸载包的依赖项,而您并不希望</p>
<ul>
<li>{cd5>污染你的系统的依赖性</li>
<li>系统范围内的<code>site-packages</code>来影响您的开发环境</li>
<li>版本冲突</li>
</ul>
<p>污染系统范围内的<code>site-packages</code>是很糟糕的,因为您在那里安装的任何包都可以用于您安装的使用系统Python的Python应用程序,即使您只是需要为您的小项目提供这种依赖性。它只是安装在一个新版本中,它重写了系统范围内的<code>site-packages</code>,并且与依赖它的${important_app}不兼容。你明白了。在</p>
<p>让您的系统范围的<code>site-packages</code>影响您的开发环境是不好的,因为您的项目可能依赖于系统Python的<code>site-packages</code>中已有的模块。因此,您忘记正确声明您的项目依赖于该模块,但一切都正常,因为它总是存在于您的本地开发框中。直到你发布你的软件包,人们试图安装它,或者把它推到生产环境中,等等。。。在干净的环境中开发会迫使您正确地声明依赖项。在</p>
<p>因此,virtualenv</strong>是一个独立的环境,具有自己的Python解释器和模块搜索路径。它基于您以前安装的Python安装,但与之隔离。在</p>
<p>要创建virtualenv,请使用<code>easy_install</code>或<code>pip</code>将其安装到系统范围的Python中,以安装<code>virtualenv</code>包:</p>
<pre class="lang-sh prettyprint-override"><code>sudo pip install virtualenv
</code></pre>
<p>请注意,这将是<em>唯一</em>的时间,您将以root用户身份(使用sudo)安装到全局站点包中。在这之后的一切都将发生在你即将创建的virtualenv中。在</p>
<p>现在创建一个virtualenv来开发您的软件包:</p>
^{pr2}$
<p>这将创建一个目录树<code>~/pyprojects/foobar-env</code>,这是您的虚拟环境。在</p>
<p>要激活virtualenv,<code>cd</code>进入其中,<code>source</code>并<code>bin/activate script</code>:</p>
<pre class="lang-sh prettyprint-override"><code>~/pyprojects $ cd foobar-env/
~/pyprojects/foobar-env $ . bin/activate
(foobar-env) ~/pyprojects/foobar-env $
</code></pre>
<p>注意前导点<code>.</code>,它是<code>source</code>shell命令的简写。还要注意提示是如何变化的:<code>(foobar-env)</code>表示您在激活的virtualenv中(并且总是需要隔离才能工作)。所以每次打开新的终端选项卡或SSH会话时都要激活env。。在</p>
<p>如果您现在在激活的env中运行<code>python</code>,那么它实际上将使用<code>~/pyprojects/foobar-env/bin/python</code>作为解释器,有自己的<code>site-packages</code>和独立的模块搜索路径。在</p>
<h2>setuptools包</h2>
<p>现在来创建你的包。基本上,您需要一个带有<code>setup.py</code>的<code>setuptools</code>包来正确声明包的元数据和依赖关系。您可以通过跟随<a href="http://pythonhosted.org/an_example_pypi_project/setuptools.html" rel="noreferrer">setuptools documentation</a>自行完成此操作,或者使用<a href="http://pythonpaste.org/script/developer.html" rel="noreferrer">Paster templates</a>创建一个包框架。要使用粘贴模板,请将<code>PasteScript</code>安装到您的virtualenv中:</p>
<pre class="lang-sh prettyprint-override"><code>pip install PasteScript
</code></pre>
<p>让我们为我们的新包创建一个源目录,使事情井井有条(也许你会想把你的项目分成几个包,或者稍后使用源代码的依赖项):</p>
<pre class="lang-sh prettyprint-override"><code>mkdir src
cd src/
</code></pre>
<p>现在要创建包,请执行以下操作:</p>
<pre class="lang-sh prettyprint-override"><code>paster create -t basic_package foobar
</code></pre>
<p>并在交互界面中回答所有问题。大多数是可选的,只需按ENTER键就可以保留默认值。在</p>
<p>这将创建一个名为<code>foobar</code>的包(或者更准确地说,setuptools发行版)。这个名字</p>
<ul>
<li>人们将使用<code>easy_install</code>或<code>pip install foobar</code>安装您的包</li>
<li>在<code>setup.py</code>中其他包将用来依赖您的包的名称</li>
<li>它将在<a href="https://pypi.python.org/pypi" rel="noreferrer">PyPi</a>上被调用</li>
</ul>
<p>在内部,您几乎总是创建一个调用相同的Python包(如“带有<code>__init__.py</code>的目录”中)。这不是必需的,顶级Python包的名称可以是任何有效的包名,但通常的惯例是将其命名为与发行版相同的名称。这就是为什么要把两者分开很重要,但并不总是那么容易。因为顶层的python包名是什么</p>
<ul>
<li>用户(或您)将使用<code>import foobar</code>或<code>from foobar import baz</code>导入您的包</li>
</ul>
<p>因此,如果您使用粘贴模板,它将已经为您创建了该目录:</p>
<pre class="lang-sh prettyprint-override"><code>cd foobar/foobar/
</code></pre>
<p>现在创建代码:</p>
<pre class="lang-sh prettyprint-override"><code>vim models.py
</code></pre>
<p><strong><code>models.py</code></strong></p>
<pre class="lang-py prettyprint-override"><code>class Page(object):
"""A dumb object wrapping a webpage.
"""
def __init__(self, content, url):
self.content = content
self.original_url = url
def __repr__(self):
return "<Page retrieved from '%s' (%s bytes)>" % (self.original_url, len(self.content))
</code></pre>
<p>以及<code>client.py</code>在使用<code>models.py</code>的同一目录中:</p>
<p><strong><code>client.py</code></strong></p>
<pre class="lang-py prettyprint-override"><code>import requests
from foobar.models import Page
url = 'http://www.stackoverflow.com'
response = requests.get(url)
page = Page(response.content, url)
print page
</code></pre>
<p>声明对^{<cd25>中<code>requests</code>模块的依赖关系:</p>
<pre class="lang-py prettyprint-override"><code> install_requires=[
# -*- Extra requirements: -*-
'setuptools',
'requests',
],
</code></pre>
<h2>版本控制</h2>
<p><code>src/foobar/</code>是您现在要置于版本控制之下的目录:</p>
<pre class="lang-sh prettyprint-override"><code>cd src/foobar/
git init
vim .gitignore
</code></pre>
<p><strong><code>.gitignore</code></strong></p>
<pre><code>*.egg-info
*.py[co]
</code></pre>
<pre class="lang-sh prettyprint-override"><code>git add .
git commit -m 'Create initial package structure.
</code></pre>
<h2>将您的软件包安装为开发鸡蛋</h2>
<p>现在是在开发模式下安装软件包的时候了:</p>
<pre class="lang-sh prettyprint-override"><code>python setup.py develop
</code></pre>
<p>这将把<code>requests</code>依赖项和<em>您的</em>包作为一个开发鸡蛋安装。所以它被链接到你的virtualenv站点包中,但是仍然存在于<code>src/foobar</code>中,你可以在那里进行更改并让它们立即在virtualenv中活动,而无需重新安装您的软件包。在</p>
<p>对于你最初的问题,使用相对路径导入:我的建议是,不要这样做。现在您已经有了一个正确的setuptools包,它已经安装并可导入,那么您当前的工作目录就不再重要了。只需执行<code>from foobar.models import Page</code>或类似操作,声明对象所在的完全限定名。这使得您的源代码更加可读和可发现,对于您自己和其他阅读您的代码的人来说。在</p>
<p>现在,您可以通过在激活的virtualenv中的任何地方执行<code>python client.py</code>来运行代码。<code>python src/foobar/foobar/client.py</code>工作正常,包安装正确,工作目录不再重要。在</p>
<p>如果您想更进一步,甚至可以为CLI脚本创建setuptools入口点。这将在virtualenv中创建一个可以从shell运行的<code>bin/something</code>脚本。在</p>
<h2>setuptools控制台脚本入口点</h2>
<p><strong><code>setup.py</code></strong></p>
<pre class="lang-py prettyprint-override"><code> entry_points='''
# -*- Entry points: -*-
[console_scripts]
run-fooobar = foobar.main:run_foobar
''',
</code></pre>
<p><strong><code>client.py</code></strong></p>
<pre class="lang-py prettyprint-override"><code>def run_client():
# ...
</code></pre>
<p><strong><code>main.py</code></strong></p>
<pre class="lang-py prettyprint-override"><code>from foobar.client import run_client
def run_foobar():
run_client()
</code></pre>
<p>重新安装软件包以激活入口点:</p>
<pre class="lang-sh prettyprint-override"><code>python setup.py develop
</code></pre>
<p>就这样,<code>bin/run-foo</code>。在</p>
<p>一旦您(或其他人)在virtualenv之外真正安装了您的包,入口点将位于<code>/usr/local/bin/run-foo</code>或类似的地方,它将自动位于<code>$PATH</code>。在</p>
<h2>进一步的步骤</h2>
<ul>
<li>创建包的发行版并将其上载到PyPi,例如使用<a href="https://pypi.python.org/pypi/zest.releaser" rel="noreferrer">^{<cd54>}</a></li>
<li>保存变更日志并对包进行版本控制</li>
<li>了解<a href="http://pythonhosted.org/setuptools/setuptools.html#declaring-dependencies" rel="noreferrer">declaring dependencies</a></li>
<li>了解<a href="https://stackoverflow.com/questions/6344076/differences-between-distribute-distutils-setuptools-and-distutils2">Differences between distribute, distutils, setuptools and distutils2</a></li>
</ul>
<p>建议阅读:</p>
<ul>
<li><a href="https://the-hitchhikers-guide-to-packaging.readthedocs.org" rel="noreferrer">The Hitchhiker’s Guide to Packaging</a></li>
<li><a href="http://www.pip-installer.org/en/latest/cookbook.html" rel="noreferrer">The pip cookbook</a></li>
</ul>