请提示我如何将用户定义的参数从命令行和 setup.cfg 配置文件传递到 distutils 的 setup.py 脚本。我想编写一个 setup.py 脚本,它接受我的包特定参数。例如:
python setup.py install -foo myfoo
由于 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,
}
)
这是一个非常简单的解决方案,您所要做的就是sys.argv
在调用 distutils 之前自己过滤并处理它setup(..)
。像这样的东西:
if "--foo" in sys.argv:
do_foo_stuff()
sys.argv.remove("--foo")
...
setup(..)
关于如何使用 distutils 执行此操作的文档非常糟糕,最终我遇到了这个:hitchhikers guide to packaging,它使用sdist
和它的user_options
. 我发现扩展 distutils参考并不是特别有用。
尽管这看起来像是使用 distutils 的“正确”方式(至少是我能找到的唯一一种模糊记录的方式)。我找不到其他答案中提到的任何东西--with
和开关。--without
这个 distutils 解决方案的问题在于,它对于我正在寻找的东西来说太复杂了(你也可能是这种情况)。添加几十行和子类sdist
对我来说是错误的。
setuptools
是的,现在是 2015 年,在两者中添加命令和选项的文档distutils
仍然很大程度上缺失。
经过几个令人沮丧的小时后,我想出了以下代码,用于向install
命令添加自定义选项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)
值得一提的是 install.run() 检查它是否被称为“本机”或已修补:
if not self._called_from_setup(inspect.currentframe()):
orig.install.run(self)
else:
self.do_egg_install()
此时,您使用以下命令注册您的命令setup
:
setup(
cmdclass={
'install': InstallCommand,
},
:
您不能真正将自定义参数传递给脚本。但是,以下事情是可能的,并且可以解决您的问题:
--with-featurename
,标准功能可以使用禁用--without-featurename
。[AFAIR 这需要安装工具]set
在 Windows 上,而在 linux/OS X ( FOO=bar python setup.py
) 上为它们添加前缀。cmd_class
es 扩展 distutils 来实现新功能。它们也是可链接的,因此您可以使用它来更改脚本中的变量。( python setup.py foo install
) 将在foo
命令执行之前执行它install
。希望能有所帮助。一般来说,我建议提供更多信息,您的额外参数到底应该做什么,也许有更好的解决方案可用。
我成功地使用了一种解决方法来使用类似于 totaam 建议的解决方案。我最终从 sys.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(...)
可以添加一些额外的验证来确保输入是好的,但我就是这样做的
与 totaam 给出的类似的一种快速简便的方法是使用 argparse 来获取 -foo 参数,并将剩余的参数留给 distutils.setup() 的调用。为此使用 argparse 比手动迭代 sys.argv 更好。例如,在 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 帮助。
也许你是一个像我一样没有经验的程序员,在阅读了上面所有的答案后仍然很挣扎。因此,您可能会发现另一个可能有用的示例(并解决先前答案中有关输入命令行参数的评论):
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,
},
以上内容经过测试,来自我编写的一些代码。我还包含了更详细的文档字符串,以使事情更容易理解。
至于命令行:python setup.py runClient --socket=127.0.0.1:7777
. 使用 print 语句进行的快速复查表明,run 方法确实选择了正确的参数。
我发现有用的其他资源(越来越多的例子):
https://seasonofcode.com/posts/how-to-add-custom-build-steps-and-commands-to-setuppy.html
要与两者完全兼容,python setup.py install
您pip install .
需要使用环境变量,因为pip
选项--install-option=
存在错误:
这是一个不使用的完整示例--install-option
:
import os
environment_variable_name = 'MY_ENVIRONMENT_VARIABLE'
environment_variable_value = os.environ.get( environment_variable_name, None )
if environment_variable_value is not None:
sys.stderr.write( "Using '%s=%s' environment variable!\n" % (
environment_variable_name, environment_variable_value ) )
setup(
name = 'packagename',
version = '1.0.0',
...
)
然后,您可以在 Linux 上像这样运行它:
MY_ENVIRONMENT_VARIABLE=1 pip install .
MY_ENVIRONMENT_VARIABLE=1 pip install -e .
MY_ENVIRONMENT_VARIABLE=1 python setup.py install
MY_ENVIRONMENT_VARIABLE=1 python setup.py develop
但是,如果您在 Windows 上,请像这样运行它:
set "MY_ENVIRONMENT_VARIABLE=1" && pip install .
set "MY_ENVIRONMENT_VARIABLE=1" && pip install -e .
set "MY_ENVIRONMENT_VARIABLE=1" && python setup.py install
set "MY_ENVIRONMENT_VARIABLE=1" && python setup.py develop
参考: