setuptools.setup 的名称参数的值如何影响结果?

How does the value of the name parameter to setuptools.setup affect the results?

我最近收到了一堆 Python 代码,由学术实验室的一名研究生编写,包括一个 Python 脚本和大约六个单文件 Python模块,由脚本使用。所有这些文件(脚本和模块)都在同一个目录中。

我想使用 pip 在虚拟环境中安装此代码,所以我尝试为它编写一个 setup.py 文件,这是我以前没有做过的事情。

我让这个安装工作​​,我对我放在 setup.py 中的大部分内容的含义有一个模糊的理解。

唯一的例外是 setuptools.setup 函数的 name 关键字的值。

根据我找到的文档,这个参数应该是“包的名称”,但我并没有告诉我它的值最终有多重要。换句话说,这个值是否仅对人类读者重要,或者它实际上会影响 pip install 或此命令安装的代码的工作方式吗?

因此,我不知道给这个参数赋予什么值,所以我只是想出了一个听起来合理的名字,但没有尝试让它与代码库中的其他东西匹配。令我惊讶的是,没有任何损坏!我的意思是 pip 安装完成且没有错误,并且安装的代码在虚拟环境中正确执行。

我做了一些实验,似乎我得出的任何值都同样可以。

为了下面的描述,假设我给 name 参数赋值 whatever。然后,据我所知,这唯一的影响是在与 setup.py 文件相同的目录中创建了一个名为 whatever.egg-info/ 的子目录(由 pip?) ,并且该子目录包含两个文件,其中包含字符串 whatever

这些文件之一是 whatever.egg-info/PKG-INFO,其中包含行

Name: whatever

另一个是whatever.egg-info/SOURCES.txt,它列出了几个相对路径,包括一些以whatever.egg-info/开头的。


对于 name 的值来说,也许这是一个太简单的包装问题?

问: 谁能给我一个简单的例子,其中 setuptools.setupname 参数的错误值会导致 pip install 或安装的代码失败?

来自Python packaging tutorial

  • name is the distribution name of your package. This can be any name as long as [it] only contains letters, numbers, _ , and -. It also must not already be taken on pypi.org.

(强调)

因此,此名称是 PyPI 上包的名称,也是 pip install 的参数。它独立于您的任何实际包代码,也不被其使用。

如果您使用 whatever 作为名称并将其上传到 PyPI,那么世界上任何用户都可以键入 pip install whatever 来安装您的包,他们可以在 [=21= 获取详细信息](事实上,已经被占用了!)。

编辑:

当您 运行 setup.py sdist bdist_wheel 时,您最终会得到一个 tar.gz 源存档和一个 whl 文件,其名称与您在 setuptools.setup 中提供的名称相同.然后,您可以使用它们在本地安装您的包或在 PyPI 之外以您希望的方式分发它们。

不过,即使在本地,包名称也必须是唯一的以避免冲突。如果您尝试安装两个具有相同名称和相同版本号的软件包,您将收到 Requirement already satisfied 消息并且 pip 将退出。如果版本号不匹配,现有的包将被卸载,新的包将取代它。

名称基本上是元数据,它不会直接影响您的代码,除非您正在提取元数据,或者使用 PyInstaller 之类的东西将其构建到 exe 中。

正如 jdaz 的回答所指出的,PyPI 名称冲突是一个考虑因素,但前提是您计划 upload/distribute 您的代码在 PyPI 上。 setuptools 实用程序同样适用于通过 Git、网络共享甚至拇指驱动器管理本地发行版的 Python 打包。或者,仅针对您从未打算分发的私人项目。

请注意,my_project.egg-info 文件夹中塞满了其他元数据,例如描述和版本控制。例如,您可以将当前版本信息存储在 PKG-INFO 文件中并使用:

  1. 设置工具(老派)
  2. pbr(一个与 Git 配合良好的设置工具插件 - 更多信息在 中)
  3. 或其他工具,例如较新的内置 importlib.metadata 包 (see Python 3.8 docs)

从您的脚本中以编程方式访问该版本信息(作为字符串、元组等)

其他元数据,如描述、包要求等,也可用,而 Python Package User Guide 和其他教程通常突出显示直接填写上传到 PyPI 所需信息的元数据,如果您不'计划公开分发,请随意填写您想要的内容并忽略其余部分(或自己滚动)。

序言: Python 词汇表定义了一个 package as "a Python module which can contain submodules or recursively, subpackages". What setuptools and the like create is usually referred to as a distribution 可以捆绑一个或多个包(因此参数 setup(packages=...))。我将在下文中将此含义用于术语 packagedistribution


name 参数决定了您的发行版在整个 Python 生态系统中的识别方式。它与发行版的实际布局(即它的包)无关,也与这些包中定义的任何模块无关。

The documentation 精确指定合法分发名称的构成:

The name of the distribution. The name field is the primary identifier for a distribution. A valid name consists only of ASCII letters and numbers, period, underscore and hyphen. It must start and end with a letter or number. Distribution names are limited to those which match the following regex (run with re.IGNORECASE): ^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$.

(历史:此规范在 PEP 566 to be aligned with the definition according to PEP 508. Before PEP 345 松散指定的发行版名称中进行了改进,没有施加任何限制。)

除了上述限制外,还有一些其他方面需要考虑:

  • 当您打算通过 PyPI then no distinction is made between _ and -, i.e. hello_world and hello-world are considered to be the same distribution. You also need to make sure that the distribution name is not already taken on PyPI because otherwise you won't be able to upload it (if it's occupied by an abandoned project, you can attempt to claim ownership of that project in order to be able to use the name; see PEP 541 分发您的发行版以获取更多信息时)。
  • 最重要的是,您应该确保发行版名称在您的工作环境中是唯一的,即它不会与其他发行版的名称冲突。假设您已经在虚拟环境中安装了 requests 项目,并且您决定也将您的发行版命名为 requests。然后安装你的发行版将删除已经存在的安装(即相应的包),你将无法再访问它。

顶级包名

上面的第二个要点也适用于您的发行版中的顶级包的名称。假设您有以下分布布局:

.
├── setup.py
└── testpkg
    └── __init__.py
    └── a.py

setup.py 包含:

from setuptools import setup

setup(
    name='dist-a',
    version='1.0',
    packages=['testpkg'],
)

__init__.pya.py 只是空文件。安装该发行版后,您可以通过导入 testpkg(顶级包)来访问它。

现在假设您有一个与 name='dist-b' 不同的发行版,但使用相同的 packages=['testpkg'] 并提供模块 b.py(而不是 a.py)。发生的情况是第二次安装是 over 已经存在的安装,即使用相同的物理目录(即 testpkg 恰好是两个发行版使用的包),可能替换已经存在的模块,尽管实际上安装了两个发行版:

$ pip freeze | grep dist-*
dist-a @ file:///tmp/test-a
dist-b @ file:///tmp/test-b
$ python
>>> import testpkg
>>> import testpkg.a
>>> import testpkg.b

现在卸载第一个分发版 (dist-a) 也会删除第二个分发版的内容:

$ pip uninstall dist-a
$ python
>>> import testpkg
ModuleNotFoundError: No module named 'testpkg'

因此,除了发行版名称外,确保其顶级包与已安装项目的包不冲突也很重要。正是这些顶级包充当了分发的命名空间。出于这个原因,选择一个类似于顶级包名称的发行版名称是个好主意——通常选择相同的名称。