Python是一种很棒的编程语言,但是打包是它的最弱点之一。 这是社区中众所周知的事实。 多年来,安装,导入,使用和创建程序包已有很大改进,但仍与Go和Rust等较新的语言相提并论,后者从Python和其他成熟语言的斗争中学到了很多东西。
在本教程中,您将学到有关编写,打包和分发自己的程序包所需的所有知识。
如何编写Python库
Python库是组织为Python包的Python模块的相关集合。 通常,这意味着所有模块都位于同一目录下,并且该目录位于Python搜索路径上。
让我们快速编写一个Python 3小程序包并说明所有这些概念。
病理包
Python 3具有出色的Path对象,这是对Python 2笨拙的os.path模块的巨大改进。 但是,它缺少一项关键功能-查找当前脚本的路径。 当您要查找相对于当前脚本的访问文件时,这非常重要。
在许多情况下,脚本可以安装在任何位置,因此您不能使用绝对路径,并且工作目录可以设置为任何值,因此您不能使用相对路径。 如果要访问子目录或父目录中的文件,则必须能够找出当前脚本目录。
使用Python的方法如下:
import pathlib
script_dir = pathlib.Path(__file__).parent.resolve()
要访问当前脚本目录的“数据”子目录中的“ file.txt”文件,可以使用以下代码: print(open(str(script_dir/'data/file.txt').read())
有了病理包,您就有了一个内置的script_dir方法,您可以像这样使用它:
from pathology.Path import script_dir
print(open(str(script_dir()/'data/file.txt').read())
是的,这是一口。 病理包非常简单。 它从pathlib的Path派生自己的Path类,并添加一个始终返回调用脚本路径的静态script_dir() 。
这是实现:
import pathlib
import inspect
class Path(type(pathlib.Path())):
@staticmethod
def script_dir():
print(inspect.stack()[1].filename)
p = pathlib.Path(inspect.stack()[1].filename)
return p.parent.resolve()
由于pathlib.Path的跨平台实现,您可以直接从它派生,并且必须从特定的子类( PosixPath或WindowsPath )派生。 脚本目录解析使用检查模块找到调用方,然后找到其文件名属性。
测试病理包
每当您编写的内容不只是一次性脚本时,都应该对其进行测试。 病理模块也不例外。 以下是使用标准单元测试框架的测试:
import os
import shutil
from unittest import TestCase
from pathology.path import Path
class PathTest(TestCase):
def test_script_dir(self):
expected = os.path.abspath(os.path.dirname(__file__))
actual = str(Path.script_dir())
self.assertEqual(expected, actual)
def test_file_access(self):
script_dir = os.path.abspath(os.path.dirname(__file__))
subdir = os.path.join(script_dir, 'test_data')
if Path(subdir).is_dir():
shutil.rmtree(subdir)
os.makedirs(subdir)
file_path = str(Path(subdir)/'file.txt')
content = '123'
open(file_path, 'w').write(content)
test_path = Path.script_dir()/subdir/'file.txt'
actual = open(str(test_path)).read()
self.assertEqual(content, actual)
Python路径
必须将Python软件包安装在Python搜索路径上的某个位置,以供Python模块导入。 Python搜索路径是目录列表,并且始终在sys.path
可用。 这是我当前的sys.path:
>>> print('\n'.join(sys.path))
/Users/gigi.sayfan/miniconda3/envs/py3/lib/python36.zip
/Users/gigi.sayfan/miniconda3/envs/py3/lib/python3.6
/Users/gigi.sayfan/miniconda3/envs/py3/lib/python3.6/lib-dynload
/Users/gigi.sayfan/miniconda3/envs/py3/lib/python3.6/site-packages
/Users/gigi.sayfan/miniconda3/envs/py3/lib/python3.6/site-packages/setuptools-27.2.0-py3.6.egg
请注意,输出的第一个空行代表当前目录,因此您可以从当前工作目录导入模块,无论它是什么。 您可以直接在sys.path中添加目录或从中删除目录。
您还可以定义PYTHONPATH环境变量,还有其他几种控制它的方法。 默认情况下包括标准site-packages
,这是您通过pip go安装软件包的地方。
如何打包Python库
现在我们有了代码和测试,让我们将它们打包到一个适当的库中。 Python通过设置模块提供了一种简便的方法。 您在包的根目录中创建一个名为setup.py的文件。 然后,要创建源代码分发,请运行: python setup.py sdist
要创建一个名为wheel的二进制发行版,请运行: python setup.py bdist_wheel
这是病理软件包的setup.py文件:
from setuptools import setup, find_packages
setup(name='pathology',
version='0.1',
url='https://github.com/the-gigi/pathology',
license='MIT',
author='Gigi Sayfan',
author_email='the.gigi@gmail.com',
description='Add static script_dir() method to Path',
packages=find_packages(exclude=['tests']),
long_description=open('README.md').read(),
zip_safe=False)
除了使用从setuptools
导入的find_packages()
函数查找子软件包的“ packages”项之外,它还包含许多元数据。
让我们建立一个源代码发行版:
$ python setup.py sdist
running sdist
running egg_info
creating pathology.egg-info
writing pathology.egg-info/PKG-INFO
writing dependency_links to pathology.egg-info/dependency_links.txt
writing top-level names to pathology.egg-info/top_level.txt
writing manifest file 'pathology.egg-info/SOURCES.txt'
reading manifest file 'pathology.egg-info/SOURCES.txt'
writing manifest file 'pathology.egg-info/SOURCES.txt'
warning: sdist: standard file not found: should have one of README, README.rst, README.txt
running check
creating pathology-0.1
creating pathology-0.1/pathology
creating pathology-0.1/pathology.egg-info
copying files to pathology-0.1...
copying setup.py -> pathology-0.1
copying pathology/__init__.py -> pathology-0.1/pathology
copying pathology/path.py -> pathology-0.1/pathology
copying pathology.egg-info/PKG-INFO -> pathology-0.1/pathology.egg-info
copying pathology.egg-info/SOURCES.txt -> pathology-0.1/pathology.egg-info
copying pathology.egg-info/dependency_links.txt -> pathology-0.1/pathology.egg-info
copying pathology.egg-info/not-zip-safe -> pathology-0.1/pathology.egg-info
copying pathology.egg-info/top_level.txt -> pathology-0.1/pathology.egg-info
Writing pathology-0.1/setup.cfg
creating dist
Creating tar archive
removing 'pathology-0.1' (and everything under it)
该警告是因为我使用了非标准的README.md文件。 可以忽略。 结果是dist目录下的tar压缩文件:
$ ls -la dist
total 8
drwxr-xr-x 3 gigi.sayfan gigi.sayfan 102 Apr 18 21:20 .
drwxr-xr-x 12 gigi.sayfan gigi.sayfan 408 Apr 18 21:20 ..
-rw-r--r-- 1 gigi.sayfan gigi.sayfan 1223 Apr 18 21:20 pathology-0.1.tar.gz
这是一个二进制分布:
$ python setup.py bdist_wheel
running bdist_wheel
running build
running build_py
creating build
creating build/lib
creating build/lib/pathology
copying pathology/__init__.py -> build/lib/pathology
copying pathology/path.py -> build/lib/pathology
installing to build/bdist.macosx-10.7-x86_64/wheel
running install
running install_lib
creating build/bdist.macosx-10.7-x86_64
creating build/bdist.macosx-10.7-x86_64/wheel
creating build/bdist.macosx-10.7-x86_64/wheel/pathology
copying build/lib/pathology/__init__.py -> build/bdist.macosx-10.7-x86_64/wheel/pathology
copying build/lib/pathology/path.py -> build/bdist.macosx-10.7-x86_64/wheel/pathology
running install_egg_info
running egg_info
writing pathology.egg-info/PKG-INFO
writing dependency_links to pathology.egg-info/dependency_links.txt
writing top-level names to pathology.egg-info/top_level.txt
reading manifest file 'pathology.egg-info/SOURCES.txt'
writing manifest file 'pathology.egg-info/SOURCES.txt'
Copying pathology.egg-info to build/bdist.macosx-10.7-x86_64/wheel/pathology-0.1-py3.6.egg-info
running install_scripts
creating build/bdist.macosx-10.7-x86_64/wheel/pathology-0.1.dist-info/WHEEL
病理软件包仅包含纯Python模块,因此可以构建通用软件包。 如果您的软件包包括C扩展名,则必须为每个平台构建一个单独的轮子:
$ ls -la dist
total 16
drwxr-xr-x 4 gigi.sayfan gigi.sayfan 136 Apr 18 21:24 .
drwxr-xr-x 13 gigi.sayfan gigi.sayfan 442 Apr 18 21:24 ..
-rw-r--r-- 1 gigi.sayfan gigi.sayfan 2695 Apr 18 21:24 pathology-0.1-py3-none-any.whl
-rw-r--r-- 1 gigi.sayfan gigi.sayfan 1223 Apr 18 21:20 pathology-0.1.tar.gz
要深入了解打包Python库的主题,请参阅如何编写自己的Python包 。
如何分发Python软件包
Python有一个称为PyPI(Python包索引)的中央包存储库。 当您使用pip安装Python软件包时,它将从PyPI下载该软件包(除非您指定其他存储库)。 要分发病理包,我们需要将其上传到PyPI并提供PyPI所需的一些其他元数据。 这些步骤是:
- 在PyPI上创建一个帐户(仅一次)。
- 注册您的包裹。
- 上载您的包裹。
创建一个帐户
您可以在PyPI网站上创建一个帐户。 然后在您的主目录中创建一个.pypirc文件:
[distutils]
index-servers=pypi
[pypi]
repository = https://pypi.python.org/pypi
username = the_gigi
出于测试目的,您可以在中添加一个“ pypitest”索引服务器。 pypirc文件:
[distutils]
index-servers=
pypi
pypitest
[pypitest]
repository = https://testpypi.python.org/pypi
username = the_gigi
[pypi]
repository = https://pypi.python.org/pypi
username = the_gigi
注册您的包裹
如果这是软件包的第一个版本,则需要在PyPI中注册。 使用setup.py的register命令。 它将要求您输入密码。 请注意,我在这里将其指向测试存储库:
$ python setup.py register -r pypitest
running register
running egg_info
writing pathology.egg-info/PKG-INFO
writing dependency_links to pathology.egg-info/dependency_links.txt
writing top-level names to pathology.egg-info/top_level.txt
reading manifest file 'pathology.egg-info/SOURCES.txt'
writing manifest file 'pathology.egg-info/SOURCES.txt'
running check
Password:
Registering pathology to https://testpypi.python.org/pypi
Server response (200): OK
上传您的包裹
现在已经注册了软件包,我们可以上传它了。 我建议使用twine ,它更安全。 使用pip install twine
照常pip install twine
。 然后使用麻线上传您的包裹并提供密码(如下所示):
$ twine upload -r pypitest -p <redacted> dist/*
Uploading distributions to https://testpypi.python.org/pypi
Uploading pathology-0.1-py3-none-any.whl
[================================] 5679/5679 - 00:00:02
Uploading pathology-0.1.tar.gz
[================================] 4185/4185 - 00:00:01
要更深入地了解分发软件包的主题,请查看如何共享Python软件包 。
结论
在本教程中,我们经历了编写Python库,打包并通过PyPI分发的完整过程。 此时,您应该拥有所有工具来与世界一起编写和共享您的库。
此外,请不要犹豫,看看我们在市场上有哪些出售和研究的产品 ,请使用下面的提要来问任何问题并提供宝贵的反馈。