如何在Python中从子模块安装依赖项?

时间:2015-01-23 16:02:42

标签: python git-submodules distutils

我有一个具有以下结构的Python项目(为简单起见省略了无关的源文件):

myproject/
    mysubmodule/
        setup.py
    setup.py

文件myproject/setup.py使用distutils.core.setup来安装模块myproject和相关来源。但是,myproject需要安装mysubmodule(这是一个git子模块)。所以我现在正在做的是:

myproject/$ cd mysubmodule
myproject/mysubmodule/$ python setup.py install
myproject/mysubmodule/$ cd ..
myproject/$ python setup.py install

这对客户来说太单调乏味了,特别是如果将来通过进一步的子模块扩展项目。

调用mysubmodule时,有没有办法自动安装myproject/setup.py

3 个答案:

答案 0 :(得分:3)

使用自己的mysubmodulesetup.py创建一个包,并让顶级包依赖于setup.py中的该包。这意味着您只需要使包/依赖项可用并在顶级包上运行python setup.py install

接下来的问题是如何将依赖项/包发送给客户,但这可以通过将它们放在一个目录中并在搜索依赖项时配置setup.py以包含该目录来解决。

另一种选择是“供应商”mysubmodule,这意味着将其全部包含在一个包中(不再提出问题)并让一个python setup.py install安装主包。例如,pip个供应商(包括)requests因此可以使用它而无需依赖requests个包。

答案 1 :(得分:2)

setuptools.find_packages()能够发现子模块

您的setup.py应该看起来像

from setuptools import setup, find_packages

setup(
    packages=find_packages(),
# ...
)

答案 2 :(得分:0)

当我需要包含几个尚未被部署到PiPy软件包索引中的,作为子模块克隆的第三方软件包时,我可以使用以下方法之一:

将setup.py作为子进程运行

# Find git submodules
sub_mod_bash_command = "git config --file " + \
   os.getcwd()+"/.gitmodules --get-regexp path | awk '{ print $2 }'"
bash_output = subprocess.check_output(['bash', '-c', sub_mod_bash_command])
sub_mods = [x for x in bash_output.decode("utf-8").split("\n") if x != '']

# Install submodule and it's requirements
for sub_mod in sub_mods:
   submod_setup_path = os.getcwd()+"/"+sub_mod+"/setup.py"
   if os.path.exists(submod_setup_path):

        # Run submodule setup.py file
        subprocess.call([sys.executable, "-m", "pip", "install", os.getcwd()+"/"+sub_mod+"/"])

使用setuptools的egg_info模块添加子模块要求

# Find git submodules
sub_mod_bash_command = "git config --file " + \
   os.getcwd()+"/.gitmodules --get-regexp path | awk '{ print $2 }'"
bash_output = subprocess.check_output(['bash', '-c', sub_mod_bash_command])
sub_mods = [x for x in bash_output.decode("utf-8").split("\n") if x != '']

# Extend requirements list with submodule requirements
for sub_mod in sub_mods:
   submod_setup_path = os.getcwd()+"/"+sub_mod+"/setup.py"
   if os.path.exists(submod_setup_path):

        # Generate requires.txt file for the submodule using the setuptools.egg_info module
        try:
            subprocess.call([sys.executable, submod_setup_path,"egg_info"])

            # Open egg_info generated requires.txt file
            with open(os.getcwd()+"/gqcnn.egg-info/requires.txt") as file:

                # Read requires file up till empty line
                for line in file:
                    if line.strip() == "":

                        ## Try to remove tree; if failed show an error using try...except on screen
                        try:
                            shutil.rmtree(os.getcwd()+"/gqcnn.egg-info")
                        except OSError as e:
                            print ("Error: %s - %s." % (e.filename, e.strerror))
                        break
                    requirements.append(line.strip()) # Append submodule requirement to package requirements
        except Exception as e:
            logger.warning("Submodule dependencies could not be imported. "+str(e))

不确定这是否是实现此目的的最干净的方法,但是在我测试过的所有情况下,它似乎都有效。