第三章:python项目的结构和包的创建

2023-01-31 02:01:13 创建 结构 第三章
python的圈子里,有许多人无偿得公开自己开发的程序库,使用者可以通过pip 命令来安装这些库,我们在发布时需要将其创建成一种特殊的文件,这种文件就是程序包,我们将会在本节学到程序包的制作流程:
  • python项目目录结构以及文件结构
  • 对第二章学习的留言板应用进行整理,封装成包
  • 最后学习如何将我们开发的项目发布在PyPI上,与全世界的人分享

3.1 Python项目

#使用python开发的应用程序达到一定的规模之后,必然会出现多个模块或者程序包目录,同时除了源码之外,说明性质的文本文件,管理相关程序库的元信息等都会越来越多,这些为同一个目的服务的文件,目录以及元信息,就是我们所说的项目。
一个完整的结构需要满足以下的条件:
  • 拥有一个在版本管理之下的源码目录
  • 程序信息在setup.py中定义
  • 在一个virtualenv环境中运行
#如果项目符合标准,那么它与工具之间就会有很强的亲和力,而且便于今后自己或者其他的开发者进一步开发,另外,本章所介绍的结构记录流程不但适合于个人的开发环境,也适用于团队的开发环境。

3.2 环境与工具

3.2.1 使用virtualenv搭建独立的环境

使用virtualenv为每一个项目搭建独立的环境,具有以下的优点:
  • 添加撤那个续保以及变更版本时,能将影响控制在当前的环境
  • 便于判断已经安装的程序包是否可以删除
  • 不再需要改环境时,可以直接删除整个环境
  • 一旦出现了问题,那么问题必然出现在该环境的项目上,有助于找到问题的所在
    使用pip安装外部程序库时,库会被安装到python的安装目录下,不同目的的程序库会被安装到同一目录下,不但容易导致版本冲突,而且很难分辨出来哪些程序库已经没有用了。

virtualenv的主要特征体现在下列的功能上

  • * 在virtualenv 环境中可以自由安装python ,不需要提供操作系统管理员权限*
  • * 在virtualev 环境下,可以根据目的不同安装程序库*
  • * 可以随时关闭或者打开virtualenv 环境*

virtualenv 环境搭建的数量没有上限

  • 不同的环境里面的库没有关系,相互之间没有任何关系
  • 创建virtualenv环境
virtualenv venv1
  • 激活环境
source venv/bin/activate
  • 查找对应的目录
$python
>>>import sys
>>>sys.executable
  • 关闭环境
这里写代码片(venv)$deactivate
  • 删除环境(首先需要关闭环境)
rm -R venv

文件结构与发布程序包

在这一部分,我们会尝试吧第二章中卡发的留言板应用放到P有PI上面进行公开,在这个过程中学习一下setup.py 的写法以及如何向PyPI上面上传程序包。

3.3.1 编写setup.py

setup.py 的功能,python的封装离不开setup.py,封装可以便于编写的长须供其他用户或者项目使用,封装的大部分时间都要小号在白那些setup.py上面。这个名字实在python中定好的,不可以随便更改,我们会在这个文件中定义程序包的名称,包以及依赖包的信息等元数据。另外,将程序包注册到PyPI的操作也需要通过setup.py来进行。

setup.py的命令

首先,先制作一个可以运行的setup.py的空壳
setup.py

from setuptools import setup
setup(name='guestbook')

setup.py的指令一览如下图:
这里写图片描述

下面我们来创建最基本的源码包,源码包需要通过python setup.py sdist命令来创建,如下图所示:
这里写图片描述

查看目录如下所示:
查看目录
现在,dist目录下面已经生成了guestbook-0.0.0.tar.gz文件,这个文件目前只包含setup.py。

注意!!在执行过程中我们看到了一些warning,这些waring 指出的项目最好都设置一下,我们将会在后面学会如何进行设置。

3.3.2 留言板的项目结构

首先,我们先来了解一下python项目一般的目录结构。当封装对象只有一个”.py “文件时,其结构如下所示:

/home/chengyiming/project_name
    +--MANIFEST.in
    +--README.rst
    +--packagename.py
    +--setup.py

如果封装目录下包含多个”.py “文件时,其结构如下所示:

/home/chengyiming/project_name
    +--MANIFEST.in
    +--README.rst
    +--packagename/
    |    +--__init__.py
    |    +--module.py
    |    +--templates/
    |       +--index.html
    +--setup.py

我们的留言板应用有下述文件组成:

文件路径 说明
guestbooki.py 服务器程序
guestbook.dat 提交数据文件
static/main.CSS CSS文件
templates/index.html 输出的HTML的模板,用于显示“提交/留言列表”的页面

虽然“.py”文件只有一个,但是static和templates目录下都包含文件,由于我们之前介绍的项目目录无法安装模板等文件,因此这里需要使用最后一种项目文件。最终文件安排如下表所示:

?home/chengyiming/guestbook/
    +--LICENSE.txt
    +--MANIFEST.in
    +--README.rst
    |    +--__init__.py
    |    +--static/main.css
    |    +--templates/index.html
    +--setup.py

现在我们来创建guestbook目录,将guestbook.py文件一发动到该目录下并命名为init.py。templates和static目录也要移动到guestbook目录下,guestbook.dat 不是我们要发布的东西,所以这里不需要它。
接下来,我们来实际用用这个封装用的结构

3.3.3 setup.py 与MANIFEST.in——设置程序包信息与捆绑的文件

接下来,我们将在setup.py中设置程序包的信息,然后子啊MANIFEST.in 中制定捆绑的文件,接下来依次了解一下。
setup.py
首先,我们描述一下guestbook项目的setup.py

from setuptools import setup,find_packages

setup(
    name='guestbook',
    version='1.0.0',
    packages=find_packages(),
    include_package_data=True,
    install_requires=['flask',],
)

如果一个环境能够使用pip,那么这个环境一定安装了setuptools库。一般情况下,我们习惯使用setuptools提供的含有拓展功能的setup函数,下面来了解一下各个函数的意义:

  • name
    程序包的名称,一般情况下,包名与程序名一致,但是一般情况下程序包的名字需要非常独特才好,所以一般会在前面加上发布的组织的名字,chengyiming.guestbook

  • version

    代表版本号的字符串
  • packages
    指定所有捆绑的python程序包(可以用python命令import 的目录名),例如,如果一个项目包含多级目录,那么我们需要使用下例所示的方法,列表指定所有的程序包。
packages=['guestbook','guestbook.server','guestbooki.server.dir','guestbook.storage',......]

find_packages()还可以自动搜索当前目录下的所有python程序包并返回程序包的名称,有了这个,我们便可以省去一个个列举的麻烦。

  • include_package_data
    在packages指定的python包(目录)中,除了“.py”之外的文件都称为程序包资源,这个设置用来指定是否安装了python包中所含的程序包资源。
    这里我们需要安装templates和static这两个程序包资源,所以将它们指定为True。
    这一设置并不能将程序包资源与我们要发布的程序包捆绑在一起,捆绑的方法将在MANIFEST.in中学习

  • install_requires
    列表指定依赖包,留言板应用要依赖Flask,所以在这里我们指定Flask,与requirements.txt不同,这里我们一般不指定版本。

MANIFEST.in
为了将HTML文件,CSS文件等程序包资源与程序包捆绑刚在一起,我们需要使用MANIFEST.in来制定封装对象文件。
这里我们在setup.py所在的目录下创建MANIFEST.in文件,制定封装对象文件的范围。
MANIFEST.in

recursive-include guestbook *.html,*.css

recursive-include白哦是捆绑指定目录下所有与制定类型一致的文件,在上面的代码中,我们捆绑了guestbook目录下所有与.html和.css一致的文件。
现在我们希望使用这个程序包的环境能够安装这些捆绑好的程序包资源,所以需要把钱买你提到的install_package_data指定为True,不能忘记奥~
MANIFEST.in还可以捆绑vguestbook应用不适用的非程序包资源文件,比如LICENSE.txt,在发布、程序包时最好把许可文件也捆绑进去。
假设我们使用了BSD许可,并在LICENSE.txt文件中描述了许可条款,接下来,我们需要在MANIFEST.in里面添加对他的捆绑指定。
MANIFEST.in

recursive-include guestbook *.html *.css
include LICENSE.txt

include会捆绑所有于指定类型一致的文件,所以再添加了上面的指定语句之后,LICENSES.txt就和程序包捆绑在了一起。
确定运行环境

  1. 搭建virtualenv环境及安装
cd ..
virtualenv .venv

2.如图所示安装的包
安装包
guestbook-1.0.0已经被安装到了虚拟环境中,我们可以看到,记录程序包原数据位置饿的guestbook.egg-link文件被安装到了virtualenv 环境中,easy-install.path文件中添加了guestbook的源码位置。/
这样的话,我们在其他PC或服务器上面构建环境是,就不必再一个个安装依赖包,如果今后需要添加或者更改依赖库,只需要按照i前面的流程更新setup.py,然后再执行一次pip install即可。

setup.py——创建执行命令

第二章的留言板项目是一个直接从python启动的脚本,要想让下载他的人用起来更加方便,最好生成一些用户命令,这里外婆们通过设置setup.py,让其自动生成guestbook命令。
我们在setup.py中添加了entry_points。这样在安装程序包时会自动生成guestbook命令。用户执行guestbook命令是将会调用guestbook模块的main函数。
但是guestbook/init.py中还没有main函数,所以我们需要添加这个函数,代码如下:

略
def main():
    application.run('127.0.0.1',8000)


if __name__=='__main__':
    application.run('127.0.0.1',8000,debug=True)

创建用于的发布的程序包时,执行python setup.py sdist命令
这里写图片描述

在dist目录下生成了guestbook-1.0.0.tar.gz。这个tar.gz文件中包含了guestbook/init.py,setup.py,LICENSE.txt,HTML,CSS等文件,只需要把这个文件安装到我们先安装应用的环境中,就可以运行pip install guestbook-1.0.0.tar.gz,直接从文件进行安装。

3.3.6 提交至版本库

我们先将之前的内容提交到版本库,关于hg 的命令操作,在第一章和第六章有详细的介绍,目前的目录介绍如下所示:

/home/chengyiming/guestbook/
    +--.venv/
    +--LICENSE.txt
    +--MANIFEST.in
    +--guestbook
    |    +--__init__.py
    |    +--static/main.css
    |    +--templates/index.html
    +--guestbook.dat
    +--guestbook.egg-info/
    +--setup.py

在开发python项目时,我们习惯将setup.py放在版本库最初级目录(根目录)下。这样我们就能使用pip直接从版本库进行安装。
另外,有些文件和目录是不用保存到版本库中,guestbook.dat文件的作用时记录留言板接收到的数据,这些数据没必要记录到版本库中。
guestbook.egg-info目录的作用是记录程序包的元数据,.venv可以重新生成,没必要保存在版本库
接下来,我们将除了以上三者的文件提交给版本库

cd ~/guestbook
hg init 
hg add LICENSE.txt MANIFEST.in guestbook setup.py
hg ci -m "initial"

3.3.7 README.rst——开发环境设置流程

以下介绍设置流程说明书,总结该留言板应用开发环境的搭建流程,如下:

  1. clone项目的版本库
  2. 搭建项目专用的virtualenv环境
  3. 在virtualenv环境内执行pip install (如果用于开发,执行pip install -e )

    设置流程:

hg clone https://bitbucket.org/chengyiming/guestbook
cd guestbook
virtualenv .venv
source .venv/bin/activate
(.venv)$pip install .
(.venv)$guestbook
*Running on Http://127.0.0.1:5000/

我们将上图的流程写入README.rst文件即可。
README.rst

==================
留言板应用
==================

目的
=====
练习开发通过WEB浏览器提交留言的web应用程序

工具版本
==============
:python:2.7.8
:pip:     1.5.6
:virtualenv:1.11.6


安装与启动方法
==================
从版本库获取代码,然后在该目录下搭建virtualenv环境:
$hg clone https://bitbucket.org/chengyiming/guestbook
$cd guestbook
$virtualenv .venv
$source .venv/bin/activate
(.venv)$pip install .
(.venv)$guestbook


开发流程
===========

用于开发的安装
-----------------
1.检测
2按照以下流程安装
(.venv)$pip install -e .

写完之后记得将README.rst文件提交到版本库

3.3.8 变更依赖包

留言板的依赖包是Flask,但是,我们在开发初期很难确定好一款应用所有的依赖包,有时候还会放弃当前的包而改用其他的,特别是周期段,发布频繁的项目,通常每发布一次,就会变更一次依赖包。

使用pip 更换了程序包,这一步如何告知他人

(.venv)$pip install flask
(.venv)$pip install bottle

留言板的setup.py里面记录着依赖包的信息,我们只需要更改setup.py的设置即可。如果更改了setup.py的install _requires行,需要再次执行pip install -e
即使我们从fsetup.py中删除了flask,之前安装到环境中的flask 以及其关联的程序包也不会被卸载,若想删除没有用的程序包,需要通过virtualenv –clear 。等方法重建环境。
重建环境如下所示:
(.venv)virtualenv –clear .venv    #删除.venv环境内的全部依赖库  
(.venv)virtualenv –clear .venv    #删除.venv环境内的全部依赖库  (.venv)pip install -e #根据./setup.py安装依赖库

3.3.9 通过requirements.txt固定开发版本

我们不仅可以通过setup.py管理依赖包,还可以通过requirements.txt来管理依赖库。
创建requiments.txt:
(.venv)$pip freeze >requirements.txt
其中记录了当前环境已经安装的所有程序包以及其明确的版本号

使用setiup.py管理依赖包时,没有指定版本,这是两者管理的一大区别
要想在其它环境中安装同样的程序包们,我们需要将这个文件防盗盖环境下,在安装
(.venv)$pip install -r requirements.txt
这样,环境中就安装了同样版本的程序包

3.3.10 Python setup.py bdist_wheel_制作用于wheel发布的程序包

制作wheel程序包之前,我们先来安装wheel
pipinstallwheelwheelpython setup.py bdist_wheel
ls dist/

上传到PyPI进行公开

我们之所以可以通过pip命令安装指定的程序包,是因为这些程序包都被注册到了PyPI上面,PyPI时python的官方网站,所有人都能随意上传以及下载python程序包,如果各位不介意公开自己开发的程序包,不妨将其注册在PyPI上。

相关文章