distutils:如何将用户定义的参数传递给setup.py?

时间:2021-08-15 07:30:39

Please prompt me how to pass a user-defined parameter both from the command line and setup.cfg configuration file to distutils' setup.py script. I want to write a setup.py script, which accepts my package specific parameters. For example:

请提示我如何从命令行和setup中传递用户定义的参数。cfg配置文件到distutils的设置。py脚本。我想写一个设置。py脚本,它接受我的包特定的参数。例如:

python setup.py install -foo myfoo

Thank you,
Mher

谢谢你,mh

7 个解决方案

#1


42  

As Setuptools/Distuils are horribly documented, I had problems finding the answer to this myself. But eventually I stumbled across this example. Also, this similar question was helpful. Basically, a custom command with an option would look like:

由于Setuptools/Distuils的文档很糟糕,我自己也发现了问题的答案。但最终我偶然发现了这个例子。同样,这个类似的问题也很有帮助。基本上,带有选项的自定义命令应该如下:

from distutils.core import setup, Command

class InstallCommand(Command):
    description = "Installs the foo."
    user_options = [
        ('foo=', None, 'Specify the foo to bar.'),
    ]
    def initialize_options(self):
        self.foo = None
    def finalize_options(self):
        assert self.foo in (None, 'myFoo', 'myFoo2'), 'Invalid foo!'
    def run(self):
        install_all_the_things()

setup(
    ...,
    cmdclass={
        'install': InstallCommand,
    }
)

#2


19  

Here is a very simple solution, all you have to do is filter out sys.argv and handle it yourself before you call to distutils setup(..). Something like this:

这里有一个非常简单的解决方案,你所要做的就是过滤系统。在调用distutils setup(..)之前自己处理它。是这样的:

if "--foo" in sys.argv:
    do_foo_stuff()
    sys.argv.remove("--foo")
...
setup(..)

The documentation on how to do this with distutils is terrible, eventually I came across this one: the hitchhikers guide to packaging, which uses sdist and its user_options. I find the extending distutils reference not particularly helpful.

关于如何处理distutils的文档非常糟糕,最终我遇到了这个问题:《搭便车指南》(hitchhikers guide to packaging),它使用了sdist和它的user_options。我发现扩展的distutils引用并不是特别有用。

Although this looks like the "proper" way of doing it with distutils (at least the only one that I could find that is vaguely documented). I could not find anything on --with and --without switches mentioned in the other answer.

尽管这看起来是使用distutils的“适当”方法(至少是我能找到的唯一一种模糊文档化的方法)。在另一个答案中,没有开关,我找不到任何开关。

The problem with this distutils solution is that it is just way too involved for what I am looking for (which may also be the case for you). Adding dozens of lines and subclassing sdist is just wrong for me.

这个distutils解决方案的问题是,对于我正在寻找的东西来说,它太复杂了(这可能也适用于您)。添加几十行和子类化sdist对我来说是错误的。

#3


11  

Yes, it's 2015 and the documentation for adding commands and options in both setuptools and distutils is still largely missing.

是的,它是2015年,在setuptools和distutils中添加命令和选项的文档仍然大量丢失。

After a few frustrating hours I figured out the following code for adding a custom option to the install command of setup.py:

在经历了几个令人沮丧的小时之后,我找到了下面的代码,用于在setup.py的安装命令中添加自定义选项:

from setuptools.command.install import install


class InstallCommand(install):
    user_options = install.user_options + [
        ('custom_option=', None, 'Path to something')
    ]

    def initialize_options(self):
        install.initialize_options(self)
        self.custom_option = None

    def finalize_options(self):
        #print('The custom option for install is ', self.custom_option)
        install.finalize_options(self)

    def run(self):
        global my_custom_option
        my_custom_option = self.custom_option
        install.run(self)  # OR: install.do_egg_install(self)

It's worth to mention that install.run() checks if it's called "natively" or had been patched:

值得一提的是,install.run()检查它是否被称为“本机”或已被修补:

if not self._called_from_setup(inspect.currentframe()):
    orig.install.run(self)
else:
    self.do_egg_install()

At this point you register your command with setup:

此时,您将命令注册到setup:

setup(
    cmdclass={
        'install': InstallCommand,
    },
    :

#4


10  

You can't really pass custom parameters to the script. However the following things are possible and could solve your problem:

您不能真正地将自定义参数传递给脚本。但是以下的事情是可能的,可以解决你的问题:

  • optional features can be enabled using --with-featurename, standard features can be disabled using --without-featurename. [AFAIR this requires setuptools]
  • (一家集这需要setuptools)
  • you can use environment variables, these however require to be set on windows whereas prefixing them works on linux/ OS X (FOO=bar python setup.py).
  • 您可以使用环境变量,但是这些变量需要在windows上设置,而在linux/ OS X上使用前缀(FOO=bar python setup.py)。
  • you can extend distutils with your own cmd_classes which can implement new features. They are also chainable, so you can use that to change variables in your script. (python setup.py foo install) will execute the foo command before it executes install.
  • 您可以使用自己的cmd_classes扩展distutils,这些类可以实现新的特性。它们也是可链接的,所以您可以使用它们来更改脚本中的变量。(python设置。py foo安装)将在执行安装之前执行foo命令。

Hope that helps somehow. Generally speaking I would suggest providing a bit more information what exactly your extra parameter should do, maybe there is a better solution available.

希望帮助。一般来说,我建议提供更多的信息,您的额外参数应该做什么,也许有更好的解决方案。

#5


4  

Perhaps you are an unseasoned programmer like me that still struggled after reading all the answers above. Thus, you might find another example potentially helpful (and to address the comments in previous answers about entering the command line arguments):

也许你是一个像我一样经验不足的程序员,在读完上面所有的答案后仍然在苦苦挣扎。因此,您可能会发现另一个示例可能有帮助(并在前面的答案中讨论输入命令行参数的注释):

class RunClientCommand(Command):
    """
    A command class to runs the client GUI.
    """

    description = "runs client gui"

    # The format is (long option, short option, description).
    user_options = [
        ('socket=', None, 'The socket of the server to connect (e.g. '127.0.0.1:8000')',
    ]

    def initialize_options(self):
        """
        Sets the default value for the server socket.

        The method is responsible for setting default values for
        all the options that the command supports.

        Option dependencies should not be set here.
        """
        self.socket = '127.0.0.1:8000'

    def finalize_options(self):
        """
        Overriding a required abstract method.

        The method is responsible for setting and checking the 
        final values and option dependencies for all the options 
        just before the method run is executed.

        In practice, this is where the values are assigned and verified.
        """
        pass

    def run(self):
        """
        Semantically, runs 'python src/client/view.py SERVER_SOCKET' on the
        command line.
        """
        print(self.socket)
        errno = subprocess.call([sys.executable, 'src/client/view.py ' + self.socket])
        if errno != 0:
            raise SystemExit("Unable to run client GUI!")

setup(
    # Some other omitted details
    cmdclass={
        'runClient': RunClientCommand,
    },

The above is tested and from some code I wrote. I have also included slightly more detailed docstrings to make things easier to understand.

上面的代码经过了测试,并来自于我编写的一些代码。我还包含了稍微详细一些的docstring,以便更容易理解。

As for the command line: python setup.py runClient --socket=127.0.0.1:7777. A quick double check using print statements shows that indeed the correct argument is picked up by the run method.

至于命令行:python设置。py runClient = 127.0.0.1:7777——插座。使用print语句进行快速的重复检查表明,run方法确实获取了正确的参数。

Other resources I found useful (more and more examples):

我发现其他有用的资源(越来越多的例子):

Custom distutils commands

自定义distutils命令

https://seasonofcode.com/posts/how-to-add-custom-build-steps-and-commands-to-setuppy.html

https://seasonofcode.com/posts/how-to-add-custom-build-steps-and-commands-to-setuppy.html

#6


1  

I successfully used a workaround to use a solution similar to totaam's suggestion. I ended up popping my extra arguments from the sys.argv list:

我成功地使用了一个变通方法来使用类似于totaam的建议的解决方案。最后,我从系统中取出了多余的参数。argv列表:

import sys
from distutils.core import setup
foo = 0
if '--foo' in sys.argv:
    index = sys.argv.index('--foo')
    sys.argv.pop(index)  # Removes the '--foo'
    foo = sys.argv.pop(index)  # Returns the element after the '--foo'
# The foo is now ready to use for the setup
setup(...)

Some extra validation could be added to ensure the inputs are good, but this is how I did it

可以添加一些额外的验证以确保输入是好的,但是我就是这样做的

#7


0  

A quick and easy way similar to that given by totaam would be to use argparse to grab the -foo argument and leave the remaining arguments for the call to distutils.setup(). Using argparse for this would be better than iterating through sys.argv manually imho. For instance, add this at the beginning of your setup.py:

与totaam给出的方法类似,一种快速而简单的方法是使用argparse来获取-foo参数,并保留调用distutil .setup()的剩余参数。为此使用argparse将比遍历sys要好。argv手动imho。例如,在setup.py的开头添加这个:

argparser = argparse.ArgumentParser(add_help=False)
argparser.add_argument('--foo', help='required foo argument', required=True)
args, unknown = argparser.parse_known_args()
sys.argv = [sys.argv[0]] + unknown

The add_help=False argument means that you can still get the regular setup.py help using -h (provided --foo is given).

add_help=False参数意味着您仍然可以获得常规设置。py帮助使用-h(提供-foo)。

#1


42  

As Setuptools/Distuils are horribly documented, I had problems finding the answer to this myself. But eventually I stumbled across this example. Also, this similar question was helpful. Basically, a custom command with an option would look like:

由于Setuptools/Distuils的文档很糟糕,我自己也发现了问题的答案。但最终我偶然发现了这个例子。同样,这个类似的问题也很有帮助。基本上,带有选项的自定义命令应该如下:

from distutils.core import setup, Command

class InstallCommand(Command):
    description = "Installs the foo."
    user_options = [
        ('foo=', None, 'Specify the foo to bar.'),
    ]
    def initialize_options(self):
        self.foo = None
    def finalize_options(self):
        assert self.foo in (None, 'myFoo', 'myFoo2'), 'Invalid foo!'
    def run(self):
        install_all_the_things()

setup(
    ...,
    cmdclass={
        'install': InstallCommand,
    }
)

#2


19  

Here is a very simple solution, all you have to do is filter out sys.argv and handle it yourself before you call to distutils setup(..). Something like this:

这里有一个非常简单的解决方案,你所要做的就是过滤系统。在调用distutils setup(..)之前自己处理它。是这样的:

if "--foo" in sys.argv:
    do_foo_stuff()
    sys.argv.remove("--foo")
...
setup(..)

The documentation on how to do this with distutils is terrible, eventually I came across this one: the hitchhikers guide to packaging, which uses sdist and its user_options. I find the extending distutils reference not particularly helpful.

关于如何处理distutils的文档非常糟糕,最终我遇到了这个问题:《搭便车指南》(hitchhikers guide to packaging),它使用了sdist和它的user_options。我发现扩展的distutils引用并不是特别有用。

Although this looks like the "proper" way of doing it with distutils (at least the only one that I could find that is vaguely documented). I could not find anything on --with and --without switches mentioned in the other answer.

尽管这看起来是使用distutils的“适当”方法(至少是我能找到的唯一一种模糊文档化的方法)。在另一个答案中,没有开关,我找不到任何开关。

The problem with this distutils solution is that it is just way too involved for what I am looking for (which may also be the case for you). Adding dozens of lines and subclassing sdist is just wrong for me.

这个distutils解决方案的问题是,对于我正在寻找的东西来说,它太复杂了(这可能也适用于您)。添加几十行和子类化sdist对我来说是错误的。

#3


11  

Yes, it's 2015 and the documentation for adding commands and options in both setuptools and distutils is still largely missing.

是的,它是2015年,在setuptools和distutils中添加命令和选项的文档仍然大量丢失。

After a few frustrating hours I figured out the following code for adding a custom option to the install command of setup.py:

在经历了几个令人沮丧的小时之后,我找到了下面的代码,用于在setup.py的安装命令中添加自定义选项:

from setuptools.command.install import install


class InstallCommand(install):
    user_options = install.user_options + [
        ('custom_option=', None, 'Path to something')
    ]

    def initialize_options(self):
        install.initialize_options(self)
        self.custom_option = None

    def finalize_options(self):
        #print('The custom option for install is ', self.custom_option)
        install.finalize_options(self)

    def run(self):
        global my_custom_option
        my_custom_option = self.custom_option
        install.run(self)  # OR: install.do_egg_install(self)

It's worth to mention that install.run() checks if it's called "natively" or had been patched:

值得一提的是,install.run()检查它是否被称为“本机”或已被修补:

if not self._called_from_setup(inspect.currentframe()):
    orig.install.run(self)
else:
    self.do_egg_install()

At this point you register your command with setup:

此时,您将命令注册到setup:

setup(
    cmdclass={
        'install': InstallCommand,
    },
    :

#4


10  

You can't really pass custom parameters to the script. However the following things are possible and could solve your problem:

您不能真正地将自定义参数传递给脚本。但是以下的事情是可能的,可以解决你的问题:

  • optional features can be enabled using --with-featurename, standard features can be disabled using --without-featurename. [AFAIR this requires setuptools]
  • (一家集这需要setuptools)
  • you can use environment variables, these however require to be set on windows whereas prefixing them works on linux/ OS X (FOO=bar python setup.py).
  • 您可以使用环境变量,但是这些变量需要在windows上设置,而在linux/ OS X上使用前缀(FOO=bar python setup.py)。
  • you can extend distutils with your own cmd_classes which can implement new features. They are also chainable, so you can use that to change variables in your script. (python setup.py foo install) will execute the foo command before it executes install.
  • 您可以使用自己的cmd_classes扩展distutils,这些类可以实现新的特性。它们也是可链接的,所以您可以使用它们来更改脚本中的变量。(python设置。py foo安装)将在执行安装之前执行foo命令。

Hope that helps somehow. Generally speaking I would suggest providing a bit more information what exactly your extra parameter should do, maybe there is a better solution available.

希望帮助。一般来说,我建议提供更多的信息,您的额外参数应该做什么,也许有更好的解决方案。

#5


4  

Perhaps you are an unseasoned programmer like me that still struggled after reading all the answers above. Thus, you might find another example potentially helpful (and to address the comments in previous answers about entering the command line arguments):

也许你是一个像我一样经验不足的程序员,在读完上面所有的答案后仍然在苦苦挣扎。因此,您可能会发现另一个示例可能有帮助(并在前面的答案中讨论输入命令行参数的注释):

class RunClientCommand(Command):
    """
    A command class to runs the client GUI.
    """

    description = "runs client gui"

    # The format is (long option, short option, description).
    user_options = [
        ('socket=', None, 'The socket of the server to connect (e.g. '127.0.0.1:8000')',
    ]

    def initialize_options(self):
        """
        Sets the default value for the server socket.

        The method is responsible for setting default values for
        all the options that the command supports.

        Option dependencies should not be set here.
        """
        self.socket = '127.0.0.1:8000'

    def finalize_options(self):
        """
        Overriding a required abstract method.

        The method is responsible for setting and checking the 
        final values and option dependencies for all the options 
        just before the method run is executed.

        In practice, this is where the values are assigned and verified.
        """
        pass

    def run(self):
        """
        Semantically, runs 'python src/client/view.py SERVER_SOCKET' on the
        command line.
        """
        print(self.socket)
        errno = subprocess.call([sys.executable, 'src/client/view.py ' + self.socket])
        if errno != 0:
            raise SystemExit("Unable to run client GUI!")

setup(
    # Some other omitted details
    cmdclass={
        'runClient': RunClientCommand,
    },

The above is tested and from some code I wrote. I have also included slightly more detailed docstrings to make things easier to understand.

上面的代码经过了测试,并来自于我编写的一些代码。我还包含了稍微详细一些的docstring,以便更容易理解。

As for the command line: python setup.py runClient --socket=127.0.0.1:7777. A quick double check using print statements shows that indeed the correct argument is picked up by the run method.

至于命令行:python设置。py runClient = 127.0.0.1:7777——插座。使用print语句进行快速的重复检查表明,run方法确实获取了正确的参数。

Other resources I found useful (more and more examples):

我发现其他有用的资源(越来越多的例子):

Custom distutils commands

自定义distutils命令

https://seasonofcode.com/posts/how-to-add-custom-build-steps-and-commands-to-setuppy.html

https://seasonofcode.com/posts/how-to-add-custom-build-steps-and-commands-to-setuppy.html

#6


1  

I successfully used a workaround to use a solution similar to totaam's suggestion. I ended up popping my extra arguments from the sys.argv list:

我成功地使用了一个变通方法来使用类似于totaam的建议的解决方案。最后,我从系统中取出了多余的参数。argv列表:

import sys
from distutils.core import setup
foo = 0
if '--foo' in sys.argv:
    index = sys.argv.index('--foo')
    sys.argv.pop(index)  # Removes the '--foo'
    foo = sys.argv.pop(index)  # Returns the element after the '--foo'
# The foo is now ready to use for the setup
setup(...)

Some extra validation could be added to ensure the inputs are good, but this is how I did it

可以添加一些额外的验证以确保输入是好的,但是我就是这样做的

#7


0  

A quick and easy way similar to that given by totaam would be to use argparse to grab the -foo argument and leave the remaining arguments for the call to distutils.setup(). Using argparse for this would be better than iterating through sys.argv manually imho. For instance, add this at the beginning of your setup.py:

与totaam给出的方法类似,一种快速而简单的方法是使用argparse来获取-foo参数,并保留调用distutil .setup()的剩余参数。为此使用argparse将比遍历sys要好。argv手动imho。例如,在setup.py的开头添加这个:

argparser = argparse.ArgumentParser(add_help=False)
argparser.add_argument('--foo', help='required foo argument', required=True)
args, unknown = argparser.parse_known_args()
sys.argv = [sys.argv[0]] + unknown

The add_help=False argument means that you can still get the regular setup.py help using -h (provided --foo is given).

add_help=False参数意味着您仍然可以获得常规设置。py帮助使用-h(提供-foo)。