distutils:如何将用户定义的parameter passing给setup.py?

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

python setup.py install -foo myfoo 

谢谢,
Mher

由于Setuptools / Distuils是可怕的文件,我有问题find自己的答案。 但最终我偶然发现了这个例子。 此外, 这个类似的问题是有帮助的。 基本上,带有选项的自定义命令将如下所示:

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

这是一个非常简单的解决scheme,您只需要在调用distutils setup(..)之前sys.argv筛选出sys.argvsys.argv处理。 像这样的东西:

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

关于如何用distutils做这件事的文档是非常糟糕的,最终我碰到了这个: 搭便车的人指导打包 ,使用sdist和它的user_options 。 我发现扩展distutils参考不是特别有用。

虽然这看起来像是用“distutils”(至less是我能find的唯一一个含糊不清的文档)的“正确”方法。 我找不到任何关于--with和 – 在另一个答案中提到的开关。

这个distutils解决scheme的问题在于,它只是涉及到我所寻找的(也可能是你的情况)。 添加几十行和sdist对我来说是错误的。

您无法将自定义parameter passing给脚本。 但是下面的事情是可能的,可以解决你的问题:

  • 可以使用--with-featurename来启用可选function,可以使用--without-featurename来禁用标准function。 [AFAIR这需要setuptools]
  • 你可以使用环境variables,但是这些需要在Windows上set ,而在Linux / OS X( FOO=bar python setup.py )中加上前缀。
  • 你可以用你自己的cmd_class来扩展distutils,这可以实现新的function。 它们也是可链接的,所以你可以使用它来改变脚本中的variables。 ( python setup.py foo install )会在执行install之前执行foo命令。

希望有所帮助。 一般来说,我会build议提供更多的信息,你的额外参数应该做什么,也许有更好的解决scheme。

是的,现在是2015年,在setuptoolsdistutils添加命令和选项的文档还是很less。

经过几个令人沮丧的时间,我想出了以下代码,用于将自定义选项添加到setup.pyinstall命令中:

 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) 

值得一提的是,install.run()会检查它是“本地”还是已经被修补:

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

在这一点上你注册你的命令setup

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

也许你是一个像我这样的无经验的程序员,在阅读了上面的所有答案之后仍然挣扎着。 因此,您可能会发现另一个示例可能有用( 并解决以前有关input命令行参数的回答中的注释 ):

 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 (eg '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, }, 

以上是testing,并从我写的一些代码。 我还包括了更详细的文档,使事情更容易理解。

至于命令行: python setup.py runClient --socket=127.0.0.1:7777 。 使用print语句进行快速的双重检查表明,运行方法确实可以获得正确的参数。

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

自定义distutils命令

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

我成功地使用了一种解决方法来使用类似于totaambuild议的解决scheme。 我结束了从sys.argv列表popup我的额外参数:

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

可以添加一些额外的validation,以确保input是好的,但这是我做的

类似于totaam给出的一个简单快捷的方法是使用argparse来获取-foo参数,并将其余的参数保留为distutils.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 

add_help=False参数意味着您仍然可以使用-h (提供的--foo给定)来获得常规的setup.py帮助。