如何编写 setup.py 以包含 Git 存储库作为依赖项

How to write setup.py to include a Git repository as a dependency

我正在尝试为我的包裹写 setup.py。我的包需要指定对另一个 Git 存储库的依赖。

这是我目前拥有的:

from setuptools import setup, find_packages

setup(
    name='abc',
    packages=find_packages(),
    url='https://github.abc.com/abc/myabc',
    description='This is a description for abc',
    long_description=open('README.md').read(),
    install_requires=[
        "requests==2.7.0",
        "SomePrivateLib>=0.1.0",
        ],
    dependency_links = [
     "git+git://github.abc.com/abc/SomePrivateLib.git#egg=SomePrivateLib",
    ],
    include_package_data=True,
)

当我运行:

pip install -e https://github.abc.com/abc/myabc.git#egg=analyse

我明白了

Could not find a version that satisfies the requirement SomePrivateLib>=0.1.0 (from analyse) (from versions: ) No matching distribution found for SomePrivateLib>=0.1.0 (from analyse)

我做错了什么?

注意:这个答案现在已经过时了。请查看下面@Dick Fox 的回答,了解最新说明:


你可以找到正确的方法here

dependency_links=['http://github.com/user/repo/tarball/master#egg=package-1.0']

关键不是将 link 提供给 Git 存储库,而是将 link 提供给 tarball。 Git如果您如上所示附加 /tarball/master,Hub 会为您创建 master 分支的 tarball。

This answer has been updated regularly as Python has evolved over the years. Scroll to the bottom for the most current answer, or read through to see how this has evolved.

不幸的是,另一个答案不适用于私有存储库,这是最常见的用例之一。我最终确实使用了一个看起来像这样(现已弃用)方法的 setup.py 文件:

from setuptools import setup, find_packages

setup(
    name = 'MyProject',
    version = '0.1.0',
    url = '',
    description = '',
    packages = find_packages(),
    install_requires = [
        # Github Private Repository - needs entry in `dependency_links`
        'ExampleRepo'
    ],

    dependency_links=[
        # Make sure to include the `#egg` portion so the `install_requires` recognizes the package
        'git+ssh://git@github.com/example_org/ExampleRepo.git#egg=ExampleRepo-0.1'
    ]
)

新版本的 pip 不再需要使用“dependency_links”-

from setuptools import setup, find_packages

setup(
    name = 'MyProject',
    version = '0.1.0',
    url = '',
    description = '',
    packages = find_packages(),
    install_requires = [
        # Github Private Repository
        'ExampleRepo @ git+ssh://git@github.com/example_org/ExampleRepo.git#egg=ExampleRepo-0.1'
    ]
)

但是,使用最新的 pip,您将 运行 遇到 EGG 格式处理程序的问题。这是因为虽然 egg 被忽略了,但 pip 现在正在做直接 URL 匹配,并且会认为两个 URLs,一个有 egg 片段,另一个没有,即使它们指向同一个包。因此,最好不要留下任何鸡蛋碎片。

2021 年 6 月 - setup.py

因此,最好的方法(目前到 2021 年 6 月)将 Github 的依赖项添加到你的 setup.py 中,它将与 public 和私有存储库一起使用:

from setuptools import setup, find_packages

setup(
    name = 'MyProject',
    version = '0.1.0',
    url = '',
    description = '',
    packages = find_packages(),
    install_requires = [
        # Github Private Repository
        'ExampleRepo @ git+ssh://git@github.com/example_org/ExampleRepo.git'
    ]
)

2022 年 2 月 - setup.cfg

显然 setup.py 已被弃用(尽管我猜它会存在一段时间)并且 setup.cfg 是新事物。

[metadata]
name = MyProject
version = 0.1.1


[options]
packages = :find

install_requires =
  ExampleRepo @ git+ssh://git@github.com/example_org/ExampleRepo.git

在深入研究 pip issue 3939 linked by @muon in the comments above and then the PEP-508 specification 之后,我发现使用 install_requires 中的规范模式(不再 dependency_links)通过 setup.py 成功安装了我的私人回购依赖项:

install_requires = [
  'some-pkg @ git+ssh://git@github.com/someorgname/pkg-repo-name@v1.1#egg=some-pkg',
]

@v1.1 表示在 github 上创建的发布标签,可以用分支、提交或不同类型的标签替换。

更笼统的回答:要从 requirements.txt 文件中获取信息,我会这样做:

from setuptools import setup, find_packages
from os import path

loc = path.abspath(path.dirname(__file__))

with open(loc + '/requirements.txt') as f:
    requirements = f.read().splitlines()

required = []
dependency_links = []

# Do not add to required lines pointing to Git repositories
EGG_MARK = '#egg='
for line in requirements:
    if line.startswith('-e git:') or line.startswith('-e git+') or \
            line.startswith('git:') or line.startswith('git+'):
        line = line.lstrip('-e ')  # in case that is using "-e"
        if EGG_MARK in line:
            package_name = line[line.find(EGG_MARK) + len(EGG_MARK):]
            repository = line[:line.find(EGG_MARK)]
            required.append('%s @ %s' % (package_name, repository))
            dependency_links.append(line)
        else:
            print('Dependency to a git repository should have the format:')
            print('git+ssh://git@github.com/xxxxx/xxxxxx#egg=package_name')
    else:
        required.append(line)

setup(
    name='myproject',  # Required
    version='0.0.1',  # Required
    description='Description here....',  # Required
    packages=find_packages(),  # Required
    install_requires=required,
    dependency_links=dependency_links,
)

实际上,如果你想让你的包递归安装(YourCurrentPackage 包括你的 SomePrivateLib),例如当你想将 YourCurrentPackage 包含到另一个包中时(比如 OuterPackage → YourCurrentPackage → SomePrivateLib)你需要两者:

install_requires=[
    ...,
    "SomePrivateLib @ git+ssh://github.abc.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
],
dependency_links = [
    "git+ssh://github.abc.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
]

并确保您使用版本号创建了标签。

此外,如果您的 Git 项目是私有的,并且您想将其安装在容器中,例如 Docker or GitLab runner, you will need authorized access to your repository. Please consider to use Git + HTTPS with access tokens (like on GitLab: https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html):

import os
from setuptools import setup

TOKEN_VALUE = os.getenv('EXPORTED_VAR_WITH_TOKEN')

setup(
    ....

    install_requires=[
            ...,
            f"SomePrivateLib @ git+https://gitlab-ci-token:{TOKEN_VALUE}@gitlab.server.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
    ],
    dependency_links = [
            f"git+https://gitlab-ci-token:{TOKEN_VALUE}@gitlab.server.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
    ]
)

已更新:

如果你想在 requirements.txt[=40 中有这个依赖,你必须把 #egg=SomePrivateLib 放在依赖行的末尾=] 文件。否则 pip install -r requirements.txt 对你不起作用,你会得到类似的东西:

ERROR: Could not detect requirement name for 'git+https://gitlab-ci-token:gitlabtokenvalue@gitlab.server.com/abc/SomePrivateLib.git@0.1.0', please specify one with #egg=your_package_name

如果你使用reuirements.txt,这部分负责将在python_home_dir/src[=中创建的依赖文件夹的名称40=] 以及 site-packages/

中的 egg-link 的名称

您可以在 requirements.txt 中使用环境变量将依赖项的令牌值安全地存储在您的存储库中:

requrements.txt 文件中的示例行:

....

-e git+https://gitlab-ci-token:${EXPORTED_VAR_WITH_TOKEN}@gitlab.server.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib
....

我在 GitLab 中成功地选择了这三个选项。我正在使用 GitLab 的版本 11。

选项 1 - 未指定令牌。 shell 将提示输入 username/password。

from setuptools import setup

TOKEN_VALUE = os.getenv('EXPORTED_VAR_WITH_TOKEN')

setup(
    install_requires=[
        "SomePrivateLib @ git+https://gitlab.server.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
    ]
)

选项 2 - 指定的用户访问令牌。通过转到 GitLab → 帐户右上角 → 设置 → 访问令牌生成的令牌。创建具有 read_repository 权限的令牌。

示例:

import os
from setuptools import setup

TOKEN_VALUE = os.getenv('EXPORTED_VAR_WITH_TOKEN')

setup(
    install_requires=[
        f"SomePrivateLib @ git+https://gitlab-ci-token:{TOKEN_VALUE}@gitlab.server.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
    ]
)

选项 3 - 指定了存储库级令牌。通过存储库→设置→存储库→部署令牌生成的令牌。在这里,创建一个具有 read_repository 权限的令牌。

示例:

import os
from setuptools import setup

TOKEN_USER = os.getenv('EXPORTED_TOKEN_USER')
TOKEN_VALUE = os.getenv('EXPORTED_VAR_WITH_TOKEN')

setup(
    install_requires=[
        f"SomePrivateLib @ git+https://{TOKEN_USER}:{TOKEN_VALUE}@gitlab.server.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
    ]
)

在这三者中,我能够简单地做到:“SomePrivateLib @ git+https://gitlab.server.com/abc/SomePrivateLib.git”没有#鸡蛋标记在最后。

当我 运行 python setup.py 安装时,此解决方案适用于我:

setuptools.setup(
    ...,
    install_requires=[
        'numpy',
        'pandas',
        'my_private_pkg'
        ],
    dependency_links=["git+https://github.com/[username]/[my_private_pkg].git@main#egg=my_private_pkg"],
    ...
)