【问题标题】:How does the value of the name parameter to setuptools.setup affect the results?setuptools.setup 的 name 参数值如何影响结果?
【发布时间】:2020-07-07 22:14:00
【问题描述】:

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

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

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

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

根据我找到的文档,这个参数应该是“包的名称”,但这并没有告诉我它的值最终如何重要。换句话说,这个值是否只对人类读者很重要,或者它实际上会影响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 pip setuptools


    【解决方案1】:

    序言:Python 词汇表将package 定义为“可以包含子模块或递归子包的Python 模块”。 setuptools 等创建的通常被称为distribution,它可以捆绑一个或多个包(因此参数setup(packages=...))。我将在下面的文本中对术语 packagedistribution 使用这个含义。


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

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

    分发的名称。名称字段是分配的主要标识符。有效名称仅由 ASCII 字母和数字、句点、下划线和连字符组成。它必须以字母或数字开头和结尾。分发名称仅限于与以下正则表达式匹配的名称(使用 re.IGNORECASE 运行):^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$

    (历史:此规范在PEP 566 中进行了细化,以符合PEP 508 的定义。在PEP 345 之前松散地指定了分发名称,没有施加任何限制。)

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

    • 当您打算通过PyPI 分发您的分发时,_- 之间没有区别,即hello_worldhello-world 被认为是相同的分发。您还需要确保分发名称尚未在 PyPI 上使用,否则您将无法上传它(如果它被废弃的项目占用,您可以尝试声明该项目的所有权以便能够使用该名称;有关详细信息,请参阅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)。发生的情况是,第二次安装已经存在的目录上执行,即使用相同的物理目录(即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'
    

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

    【讨论】:

      【解决方案2】:

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

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

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

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

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

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

      【讨论】:

        【解决方案3】:

        来自Python packaging tutorial

        • name 是您的软件包的发行版名称。这可以是任何名称,只要 [it] 仅包含字母、数字、_ 和 -。 也不能在 pypi.org 上使用

        (已添加重点)

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

        如果您使用 whatever 作为名称并将其上传到 PyPI,那么世界上的任何用户都可以输入 pip install whatever 来安装您的包,他们可以通过 https://pypi.org/project/whatever/ 获取详细信息(实际上,已经拍了!)。

        编辑:

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-07-13
          • 2019-06-11
          • 1970-01-01
          • 2021-09-14
          • 1970-01-01
          相关资源
          最近更新 更多