除非 --public 传递给上传命令,否则如何禁用将包上传到 PyPi [英] How to disable uploading a package to PyPi unless --public is passed to the upload command

查看:48
本文介绍了除非 --public 传递给上传命令,否则如何禁用将包上传到 PyPi的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm developing packages and uploading development/testing/etc versions of my packages to a local devpi server.

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

In order to prevent an accidental upload to PyPi, I'm adopted the common practice of:

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

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

which works great, but what about when I'm finally ready to upload the package to PyPi?

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

I've come up with a totally ugly, but simple hack which requires that I define the classifiers as a global variable outside of the setup() call which looks like:

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,
      ...)

另一个可能更简单的选择是仅注释掉私人 :: 请勿上传",但这似乎并不比我的 hack 更专业.

Another, and perhaps simpler option is to merely comment out the "Private :: Do not Upload", but that doesn't seem any more professional than my hack.

喜欢要做的是创建一个名为 SafeUpload 的上传命令的正确子类,并让它检查 --public> 命令行选项.也许,因为在上传之前可能存在构建,SafeBuild 可能是更好的选择.

What I'd like to do is create a proper subclass of the upload command called SafeUpload and have it check for the --public cmd-line option. Perhaps, as a build may exist prior to uploading, SafeBuild might be a better option.

很遗憾,我无法理解有关创建自定义命令的 setuptools 文档.

Unfortunately, I'm having trouble understanding the setuptools documentation on creating custom commands.

有谁知道如何实现这一点?我不清楚自定义命令是否可以访问传递给 setup() 的参数,即它是否可以直接操作传递给 setup()<的 classifiers/code>,或者如果它要求命令的用户遵循将 CLASSIFIERS 定义为全局变量的约定yuck?

Does anyone have any idea how to implement this? It's not clear to me if a custom command has access to the parameters passed to setup(), i.e. could it directly manipulate the classifiers passed to setup(), or would if it require that a user of the command follow the convention of defining of CLASSIFIERS as a global variable yuck?

推荐答案

倒退你的问题;虽然它真的很广泛,但主题仍然足够局限.

Going backwards on your questions; while it's really broad, the topic is still constrained enough.

我可以告诉你,分类器没有被操纵,而是通过 egg_info 命令从文件中读取然后写入到 PKG-INFO 文件中,这反过来看起来对于所有 egg_info.writers 入口点,setuptools.command.egg_info:write_pkg_info 函数将进行实际写入.据我所知,尝试在外部利用该分类器不是一个好方法,但是您可以通过 setuptools 覆盖所有内容任何内容code> 以便您可以创建自己的 write_pkg_info 函数,弄清楚如何读取元数据(您可以在主distutils.command.upload:upload.upload_file 方法)并在 upload_file 最终读取它之前进一步操作它.在这一点上,您可能会认为操纵和使用这个系统会很烦人.

I can tell you that the classifiers are not manipulated, but rather read from the and then written to PKG-INFO file by the egg_info command, which in turn looks for all egg_info.writers entry_points which the setuptools.command.egg_info:write_pkg_info function will do the actual writing. As far as I can tell, trying to leverage that Classifier outside will not be a great way, however you can override everything and anything you want through setuptools so you can make your own write_pkg_info function, figure out how to read the metadata (which you can see in the main distutils.command.upload:upload.upload_file method) and manipulate that further before upload_file finally reads it. At this point you probably are thinking that manipulating and working with this system is going to be rather annoying.

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

As I mentioned though, everything can be overridden. You can make an upload command that take the public flag, like so:

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)

随附的 setup.py 可能看起来像这样.

The accompanied setup.py might look something like this.

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',
        ],
    },
)

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

Install that as a package into your environment and the upload command will be replaced by your version. Example run:

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

使用公共标志重试

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

这很好,因为我根本没有创建任何 dist 文件.您当然可以通过重写 upload_file 方法(在您的代码中复制)并更改部分以在您的子类中执行您想要的操作(例如在那里注入私有分类器),由您决定.

Which is fine, since I didn't create any dist files at all. You could of course further extend that command by rewriting the upload_file method (make a copy in your code) and change the parts to do what you want in your subclass (like injecting the private classifier there), up to you.

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

You might also be wondering why the class names are in lower case (violation of pep8), this is due to legacy stuff and how the help for a given command is generated.

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

使用正确"命名的类(例如 SafeUpload;记住还要更新 setup.py 中的 entry_point 以指向这个新的类名)

Using a "properly" named class (e.g. SafeUpload; remember to also update the entry_point in the setup.py to point to this new class name)

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

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

of course if this output is the intent, the standard class naming convention can be used instead.

尽管说实话,您根本不应该在生产中指定上传,而是在构建服务器上作为 post-push hook 的一部分执行此操作,因此当项目被推送(或标记)时,构建就完成了并将文件加载到您的私人服务器上,然后只有进一步的手动干预(如果推送了特定标签,则自动干预)才会将包上传到 pypi.然而,上面的例子应该能让你开始做你最初打算做的事情.

Though to be perfectly honest, you should not specify upload at all on your production, but rather do this on your build servers as part of post-push hook, so when the project is pushed (or tagged), build is done and the file is loaded onto your private servers, and then only further manual intervention (or automatic if specific tags are pushed) will then get the package up to pypi. However the above example should get you started in what you originally set out to do.

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

One last thing: you can just change self.repository to your private devpi location, if the --public flag is not set. You could either override this before calling the orig.upload_file method (through your customized version), or do it in run; so rather than quitting, your code could just verify that the repository url is not the public PyPI instance. Or alternatively, manipulate the distribution metadata (i.e. the classifiers) via self.distribution.metadata (self being the upload instance). You can of course create a completely new command to play with this to your hearts content (by creating a new Command subclass, and add a new entry_point for that).

这篇关于除非 --public 传递给上传命令,否则如何禁用将包上传到 PyPi的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆