除非将--public传递给upload命令,否则如何禁止将包上传到PyPi

时间:2016-09-15 22:01:06

标签: python setuptools pypi devpi

我正在开发软件包并将我的软件包的开发/测试/等版本上传到本地devpi服务器。

为了防止意外上传到PyPi,我采用了以下的常规做法:

setup(...,
      classifiers=[
        "Programming Language :: Python",
        "Programming Language :: Python :: 2",
        "Programming Language :: Python :: 2.7",
        "Private :: Do not Upload"
     ],
     ...)

效果很好,但是当我最终准备将包上传到PyPi时呢?

我想出了一个完全丑陋但简单的hack,它要求我将分类器定义为setup()调用之外的全局变量,如下所示:

CLASSIFIERS = [
    "Programming Language :: Python",
    "Programming Language :: Python :: 2",
    "Programming Language :: Python :: 2.7"
]


if "--public" not in sys.argv:
     CLASSIFIERS.append("Private :: Do Not Upload")
else:
     sys.argv.remove("--public")

setup(...
      classifiers=CLASSIFIERS,
      ...)

另一个,也许更简单的选择是仅仅注释掉“私人::不上传”,但这似乎不比我的黑客更专业。

要做的是创建一个名为SafeUpload的上传命令的正确子类,并让它检查--public cmd-line选项。也许,由于在上传之前可能存在构建,SafeBuild可能是更好的选择。

不幸的是,我无法理解有关创建自定义命令的setuptools文档。

有没有人知道如何实现这个?我不清楚自定义命令是否可以访问传递给setup()的参数,即它是否可以直接操作传递给classifiers的{​​{1}},或者如果它需要用户该命令遵循将CLASSIFIERS定义为全局变量 yuck 的约定?

1 个答案:

答案 0 :(得分:2)

回顾你的问题;虽然它非常广泛,但主题仍然受到限制。

我可以告诉你,分类器不是被操作的,而是从PKG-INFO命令读取然后写入egg_info文件,而egg_info.writers命令又查找所有setuptools entry_points其中setuptools.command.egg_info:write_pkg_info函数将进行实际编写。据我所知,尝试在外面利用该分类器不是一个好方法,但是你可以通过write_pkg_info覆盖你想要的所有任何所以您可以创建自己的from distutils.log import warn from distutils.command.upload import upload as orig # alternatively, for later versions of setuptools: # from setuptools.command.upload import upload as orig class upload(orig): description = "customized upload command" user_options = orig.user_options + [ ('public', None, 'make package public on pypi'), ] def initialize_options(self): orig.initialize_options(self) self.public = False def run(self): if not self.public: warn('not public, not uploading') return return orig.run(self) 函数,找出如何read the metadata(您可以在主distutils.command.upload:upload.upload_file方法中看到)并在upload_file最终读取之前进一步操作。此时你可能认为操纵和使用这个系统将会非常烦人。

正如我所提到的,一切都可以被覆盖。您可以创建一个带有公共标志的上传命令,如下所示:

setup.py

伴随的from setuptools import setup setup( name='my_pypi_uploader', version='0.0', description='"safer" pypi uploader', py_modules=['my_pypi_uploader'], # assuming above file is my_py_uploader.py entry_points={ 'distutils.commands': [ 'upload = my_pypi_uploader:upload', ], }, ) 可能看起来像这样。

$ python setup.py upload
running upload
not public, not uploading

将其作为包安装到您的环境中,upload命令将替换为您的版本。示例运行:

$ python setup.py upload --public
running upload
error: No dist file created in earlier command

使用公共标志再次尝试

$ python setup.py upload --help
...
Options for 'upload' command:

哪个好,因为我根本没有创建任何dist文件。您当然可以通过重写upload_file方法(在代码中复制)并更改部件以在子类中执行您想要的操作(如在那里注入私有分类器)来进一步扩展该命令,由您决定。

你可能也想知道为什么类名是小写的(违反pep8),这是由于遗留的东西以及如何生成给定命令的帮助。

SafeUpload

正确使用""命名类(例如entry_point;还记得同时更新setup.py中的$ python setup.py upload --help ... Options for 'SafeUpload' command: 以指向此新类名称

self.repository

当然,如果此输出是intent,则可以使用标准类命名约定。

虽然说实话,但是你不应该在生产中指定上传,而是在构建服务器上作为推后挂钩的一部分来执行此操作,因此当推送(或标记)项目时,构建完成并且文件被加载到您的私人服务器上,然后只有进一步的手动干预(或者如果推送特定标签,则自动干预)将使包到达pypi。但是,上面的示例应该让您从最初的目标开始。

最后一件事:如果未设置--public标志,那么可以orig.upload_file更改为您的私有devpi位置。您可以在调用run方法(通过自定义版本)之前覆盖此方法,或者在self.distribution.metadata中执行此操作;因此,您的代码可以只验证存储库URL不是公共PyPI实例,而不是退出。或者,通过selfuploadCommand实例)操纵分发元数据(即分类器)。你当然可以创建一个全新的命令来与你的心灵内容一起玩(通过创建一个新的XMLMessage.setApplicationMessageId()子类,并为此添加一个新的entry_point。)