setuptools setup.pyファイル内のinstall_requires kwargの参照requirements.txt


279

requirements.txtTravis-CIで使用しているファイルがあります。との両方requirements.txtで要件を複製するのはばかげているようですsetup.py。そのため、ファイルハンドルをのinstall_requireskwargに渡したいと思っていましたsetuptools.setup

これは可能ですか?もしそうなら、私はそれをどうやってやるべきですか?

これが私のrequirements.txtファイルです:

guessit>=0.5.2
tvdb_api>=1.8.2
hachoir-metadata>=1.3.3
hachoir-core>=1.3.3
hachoir-parser>=1.3.4

4
install_requiresパッケージの動作に必要なパッケージへの依存関係を宣言するために使用され、パッケージの開発者によって使用されます。一方requirements.txt、環境のインストールを自動化するために使用されます。これにより、追加のソフトウェアのインストールとバージョンの固定が可能になり、システム管理者がパッケージ。彼らの役割と対象読者は大幅に異なるため、OPの希望のようにそれらを組み合わせようとすることは、本当の設計ミスです。
Zart

7
私の2セント。setup.pyでrequirements.txtを使用しないでください。目的は異なります。aremad.io/ 2013/07
Philippe Ombredanne

3
複雑な答えがたくさんあります。プレーンオールドの何が問題になってい[line.strip() for line in open("requirements.txt").readlines()]ますか?
フェリペSSシュナイダー

これを行うことはお勧めしません。本当に必要な場合でも、それは簡単です:setuptoolsの自体はすでに必要なすべてのものがあるpkg_resources.parse_requirements()
sinoroc

回答:


246

あなたはそれを周りに反転し、リスト内の依存関係をすることができますsetup.pyし、単一の文字がある-ドット.で- requirements.txt代わりに。


または、アドバイスされrequirements.txtていなくても、次のハック(でテスト)を使用してファイルを解析できます(URLで外部要件を参照していない場合pip 9.0.1)。

install_reqs = parse_requirements('requirements.txt', session='hack')

ただし、これは環境マーカーをフィルターしません。


古いバージョンのpip、具体的には6.0より古いバージョンでは、これを実現するために使用できるパブリックAPIがあります。要件ファイルにはコメント(#)を含めることができ、その他のファイル(--requirementまたは-r)を含めることができます。したがって、本当に解析したい場合requirements.txtは、pipパーサーを使用できます。

from pip.req import parse_requirements

# parse_requirements() returns generator of pip.req.InstallRequirement objects
install_reqs = parse_requirements(<requirements_path>)

# reqs is a list of requirement
# e.g. ['django==1.5.1', 'mezzanine==1.4.6']
reqs = [str(ir.req) for ir in install_reqs]

setup(
    ...
    install_requires=reqs
)

26
ユーザーがpipをインストールしていない場合はどうなりますか?カブーム?
Gringo Suave 2013

82
@GringoSuaveユーザーがpipをインストールしていない場合、最初にインストールする必要があります。
guettli 2013

7
pypi以外のパッケージを指す-eまたは-f(「編集可能な」Gitリポジトリ)行がある場合は、要件ファイルにURLを指定する必要もあります。これを使用:setup(..., dependency_links=[str(req_line.url) for req_line in parse_requirements(<requirements_path>)], ...)
ホブ

91
あなたは本当にこれをしたくありません。ピップメンテナーとして話すピップは、このようなAPIとして呼び出されることをまったくサポートしていません。実際、pip 1.6(現時点では次のバージョン)がこの関数を移動します。
ドナルドスタッフ2014年

26
必要があれば、これはもはや受け入れられた答えではないはずです。明らかに壊れています。それが機能したときでさえ、それは明らかに不必要です。以来pipからの依存関係を解析デフォルトsetup.pyが存在しない場合にrequirements.txt単純な答え鋭くによって指摘東武以下がすることです内のすべての依存関係を一覧表示setup.pyし、削除requirements.txt両方を必要とするアプリケーションの場合は、依存関係リストrequirements.txtを単純に.文字だけに減らします。できました。
Cecil Curry 2016年

194

一見したところrequirements.txtsetup.py、ばかげて重複、フォームは似ていますが、目的の機能が大きく異なることを理解することが重要です。

依存関係を指定するときのパッケージ作成者の目標は、「このパッケージをインストールする場所はどこでも、これらはこのパッケージが機能するために必要な他のパッケージです」と言うことです。

対照的に、展開の作成者(異なる時点で同じ人物である可能性があります)は、「ここにまとめてテストし、今インストールする必要があるパッケージのリストを示します」と言うという点で、別の仕事をしています。

パッケージ作成者はさまざまなシナリオで作成します。なぜなら、彼らは自分たちの仕事を知らない方法で使用するためにそこに出し、パッケージと一緒にインストールされるパッケージを知る方法がないからです。良い隣人となり、他のパッケージとの依存関係バージョンの競合を回避するために、彼らは、機能する可能性がある限り広い範囲の依存関係バージョンを指定する必要があります。これは何install_requiressetup.pyありません。

展開の作成者は、非常に異なる非常に具体的な目標(特定のコンピューターにインストールされているインストール済みのアプリケーションまたはサービスの単一のインスタンス)について記述します。デプロイメントを正確に制御し、適切なパッケージが確実にテストおよびデプロイされるようにするために、デプロイメント作成者は、インストールするすべてのパッケージの正確なバージョンとソースの場所(依存関係と依存関係を含む)を指定する必要があります。この仕様では、デプロイメントを複数のマシンに繰り返し適用したり、テストマシンでテストしたりできます。デプロイメント作成者は、同じパッケージが毎回デプロイされることを確信できます。これはa requirements.txtが行うことです。

どちらもパッケージとバージョンの大きなリストのように見えますが、これら2つには非常に異なるジョブがあることがわかります。そして、これを混同して間違いを犯すことは間違いなく簡単です!しかし、これについて考える正しい方法requirements.txtは、すべてのさまざまなsetup.pyパッケージファイルの要件によってもたらされる「質問」に対する「答え」です。手で書くのではなくsetup.py、目的のパッケージのセットに含まれるすべてのファイルを調べ、すべての要件に適合すると思われるパッケージのセットを見つけ、インストール後に「凍結」するようにpipに指示することで生成されることがよくあります。 "パッケージのリストをテキストファイルに保存します(これはpip freeze名前の由来です)。

したがって、要点:

  • setup.pyまだ実行可能な最も緩やかな依存バージョンを宣言する必要があります。その仕事は、特定のパッケージが何に対応できるかを言うことです。
  • requirements.txtインストールジョブ全体を定義するデプロイメントマニフェストであり、1つのパッケージに関連付けられているとは考えないでください。その役割は、デプロイメントを機能させるために必要なすべてのパッケージの完全なリストを宣言することです。
  • これら2つは内容が異なり、存在する理由があるため、一方を他方に単純にコピーすることはできません。

参照:


10
これは、パッケージのインストールと呼ばれるその混乱の中でいくつかの順序を付けさせた最高の説明の1つです。:)
Kounavi 2015年

6
開発者がrequirements.txtインストールまたはテストのための具体的な/凍結された要件を含むパッケージのソースとともにバージョン管理を維持する理由がまだはっきりしていません。確かにsetup.pyこのプロジェクト自体の中で、この目的のために使用することができますか?プロジェクトの管理をサポートするために使用されるツール(たとえば、リファクタリング、リリースの作成など)にこのようなファイルを使用することしか想像できません。
サムブライトマン

2
@samBrightman完全に同意します。ライブラリパッケージまたはアプリケーションパッケージは、requirements.txtファイルをコードとともにリポジトリにコミットするべきではないと思います。これは、ビルドテスト中に生成されたアーティファクトであり、ビルドマニフェストを文書化し、最終的にデプロイメントアーティファクトを生成するために使用する必要があると思います。
ジョナサンハンソン

6
つまりrequirements.txt、通常はビルドプロセス自体では使用されませんが、特定のビルドを作成した世界の状態に関するより多くのドキュメントがあるということですか。それは理にかなっている。ただし、いくつかのシステムは複製に依存しているようです。Travisは、virtualenvにいくつかのデフォルト(古い)パッケージをインストールし、を使用するように言っていますrequirements.txt。依存関係が遅くとも使用されていることを確認する方法を尋ねるとsetup.py、人々は私が使用するべきだと主張しますrequirements.txt
サムブライトマン

2
これから得られる最良のアドバイスは、自分に合ったモデルを見つけ、それを文書化して、作業する全員が理解できるようにすることです。なぜあなたが少しずつやっているのか、そしてそれが本当にあなたのユースケースにとって理にかなっているかを考えてください。また、状況が改善した場合に備えて、Pythonでのビルド、パッケージ化、およびパブリッシングの現在の状態について、できる限りよく読んでください。しかし、息を止めないでください。
Jonathan Hanson

89

ファイルハンドルを受け取ることはできません。install_requires引数ができるだけで、文字列または文字列のリストです

もちろん、セットアップスクリプトでファイルを読み取り、文字列のリストとしてに渡すことができますinstall_requires

import os
from setuptools import setup

with open('requirements.txt') as f:
    required = f.read().splitlines()

setup(...
install_requires=required,
...)

5
これは有用ですが、要件の仕様を宣言型から命令型に変更します。このため、一部のツールでは要件が何であるかを見つけることができません。たとえば、PyCharmはで指定されたすべての要件の自動インストールを提供しますinstall_requires。ただし、宣言構文を使用しない場合は機能しません。
Piotr Dobrogost 2013

55
@PiotrDobrogostおそらくPyCharm開発者はプログラムを修正する必要があります。setup.py実行する必要があるプログラムであり、解析する必要があるデータファイルではありません。これでこの答えが悪くなることはありません。
Fredrick Brennan

5
考えられる問題を指摘しています。この答えは完璧です。コードに「隠されている」情報に問題があるのはPyCharmだけではありません。これは普遍的な問題であり、Pythonパッケージングでのメタデータの宣言的な仕様に向けた一般的な動きがあります。
Piotr Dobrogost 2013

32
限り、あなたが入れたとしてとして罰金を作品include requirements.txtあなたにMANIFEST.inまたはあなたがソース配布から自分のライブラリをインストールすることはできません。
Pankrat 2013年

4
私はこれが古い質問であることを知っていますが、少なくとも今日では、PyCharmを構成して要件ファイルを解析することができます-環境設定->ツール-> Python統合ツール->パッケージ要件ファイル
lekksi

64

要件ファイルは、拡張されたpip形式を使用します。これはsetup.py、一部の依存関係の元となる正確なURLを指定したり、pip freezeパッケージセット全体を既知の動作に固定するための出力を指定したりするなど、より強い制約で補完する必要がある場合にのみ役立ちます。バージョン。追加の制約が必要ない場合は、のみを使用してくださいsetup.pyrequirements.txtとにかく本当に出荷する必要があると思われる場合は、1行にすることができます。

.

これは有効setup.pyで、同じディレクトリにあるのコンテンツを正確に参照します。


9
しかし、この場合、それも私のアプリをインストールしようとします。必要なく、install_requiresのみをインストールしたい場合はどうなりますか?
2016

2
@ffeastが求めていることを詳しく説明するために、setup.pyにのみ要件がある場合pip install -r requirements.txt 、パッケージ自体をインストールせずに要件(と同等)をインストールする方法はありますか?
haridsv 2017年

1
@ffeast @haridsv -e .で十分です。このページを確認してください:caremad.io/posts/2013/07/setup-vs-requirement
dexhunter

4
@ DexD.Hunterは、アプリ自体をインストールしようとします。これは、私たちが望むものではありません
ffeast

38

質問への正確な回答ではありませんが、この問題を適切に理解するには、Donald Stufftのブログ投稿(https://caremad.io/2013/07/setup-vs-requirement/)をお勧めします。私はそれを大成功に使用してきました。

つまり、requirements.txtsetup.py代替ではなく、展開を補完するものです。でパッケージの依存関係の適切な抽象化を維持しますsetup.pyrequirements.txt開発、テスト、または本番用にパッケージの依存関係の特定のバージョンをフェッチするように設定します。

たとえば、以下のリポジトリに含まれるパッケージを使用しますdeps/

# fetch specific dependencies
--no-index
--find-links deps/

# install package
# NOTE: -e . for editable mode
.

pipはパッケージを実行setup.pyし、で宣言された特定バージョンの依存関係をインストールしinstall_requiresます。重複はなく、両方のアーティファクトの目的は保持されます。


7
他の人がを介してインストールするパッケージを提供したい場合、これは機能しませんpip install my-package。my-packageの依存関係がmy-package / setup.pyにリストされていない場合、それらはによってインストールされませんpip install my-package。setup.pyで明示的に指定せずに、依存関係を含む他のパッケージを提供する方法を決定できませんでした。誰かが要件ファイルをダウンロードして手動でを呼び出さずにmy-package +依存関係をインストールできるようにしながら、DRYを維持する方法を誰かが見つけたかどうか知りたいですpip install -r my-package/requirements.txt
マリナ2014年

2
@Malinaここのパッケージはなしで完全にインストールできrequirements.txtます。それがポイントです。より明確にするために質問を更新しました。廃止されたブログ投稿のリンクも更新されました。
有名なガーキン2015年

したがって、setup.pyを実行すると、stup.pyにリストされているファイルの特定のバージョンに対してrequirements.txtが呼び出されますか?
dtracers 2016年

これは@dtracersを回避するもう1つの方法です。requirements.txtは、それ自体がパッケージをポイントしており、setup.pyの依存関係を取得できます。したがって、要件を使用してインストールする場合は機能し、pipを介してインストールする場合も機能します。どちらの場合も、setup.pyの依存関係を使用しますが、requirements.txtを使用すると、さらに多くのものをインストールできます
smido

20

parse_requirementspip APIは公開されておらず、サポートされていないため、使用には問題があります。pip 1.6では、その関数は実際に動いているため、既存の使用法は機能しなくなる可能性があります。

間の重複を排除するため、より信頼性の高い方法setup.pyrequirements.txt、特定のお依存関係であるsetup.py入れた後、-e .あなたの中にrequirements.txtファイル。pipそれがより良い方法である理由について、開発者の1人からの情報は、https//caremad.io/blog/setup-vs-requirement/から入手できます。


@Tommy試してみてください:caremad.io/2013/07/setup-vs-requirementこれは、別の回答で投稿されたものと同じリンクです。
アミット

18

上記の他の回答のほとんどは、pipのAPIの現在のバージョンでは機能しません。現在のバージョンのpipでこれを行う正しい*方法は次のとおりです(執筆時点では6.0.8、これも7.1.2で機能します。バージョンはpip -Vで確認できます)。

from pip.req import parse_requirements
from pip.download import PipSession

install_reqs = parse_requirements(<requirements_path>, session=PipSession())

reqs = [str(ir.req) for ir in install_reqs]

setup(
    ...
    install_requires=reqs
    ....
)

*正解です。現在のpipでparse_requirementsを使用する方法です。上記のポスターが言ったように、pipは実際にはAPIを維持していないため、それはおそらくそれを行うための最良の方法ではありません。


14

Travisに現在のパッケージをインストールします。これにより、requirements.txtファイルの使用が回避されます。例えば:

language: python
python:
  - "2.7"
  - "2.6"
install:
  - pip install -q -e .
script:
  - python runtests.py

2
これは、「正しい」と「実用的な」の最良の組み合わせです。テストに合格した後、Travisを使用してrequirements.txtを生成し、pip freezeそのファイルをアーティファクト(S3など)としてどこかにエクスポートできる場合は、正確にインストールしたものを繰り返しインストールできる優れた方法があります。テスト済み。
Jonathan Hanson、

4

from pip.req import parse_requirements 私にとってはうまくいきませんでした、そしてそれは私のrequirements.txtの空白行のためであると思います、しかしこの関数はうまくいきます

def parse_requirements(requirements):
    with open(requirements) as f:
        return [l.strip('\n') for l in f if l.strip('\n') and not l.startswith('#')]

reqs = parse_requirements(<requirements_path>)

setup(
    ...
    install_requires=reqs,
    ...
)

4

ユーザーにpipのインストールを強制したくない場合は、次のようにしてその動作をエミュレートできます。

import sys

from os import path as p

try:
    from setuptools import setup, find_packages
except ImportError:
    from distutils.core import setup, find_packages


def read(filename, parent=None):
    parent = (parent or __file__)

    try:
        with open(p.join(p.dirname(parent), filename)) as f:
            return f.read()
    except IOError:
        return ''


def parse_requirements(filename, parent=None):
    parent = (parent or __file__)
    filepath = p.join(p.dirname(parent), filename)
    content = read(filename, parent)

    for line_number, line in enumerate(content.splitlines(), 1):
        candidate = line.strip()

        if candidate.startswith('-r'):
            for item in parse_requirements(candidate[2:].strip(), filepath):
                yield item
        else:
            yield candidate

setup(
...
    install_requires=list(parse_requirements('requirements.txt'))
)

4

次のインターフェイスは、pip 10で非推奨になりました。

from pip.req import parse_requirements
from pip.download import PipSession

だから私はそれを単純なテキスト解析に切り替えました:

with open('requirements.txt', 'r') as f:
    install_reqs = [
        s for s in [
            line.split('#', 1)[0].strip(' \t\n') for line in f
        ] if s != ''
    ]

この単純なアプローチは、90%以上の時間で機能します。Python 3.6+を使用している人のために、そのバリエーションである回答をpathlib書きました。
Acumenus

3

この単純なアプローチは、要件ファイルをから読み取りますsetup.pyDmitiry Sの回答のバリエーションです。この回答はPython 3.6以降とのみ互換性があります。

DSごとに、requirements.txt特定のバージョン番号で具体的な要件を文書化できますが、setup.py文書化できますが、バージョン範囲が緩い抽象的な要件を文書化できます。

以下は、私の抜粋ですsetup.py

import distutils.text_file
from pathlib import Path
from typing import List

def _parse_requirements(filename: str) -> List[str]:
    """Return requirements from requirements file."""
    # Ref: https://stackoverflow.com/a/42033122/
    return distutils.text_file.TextFile(filename=str(Path(__file__).with_name(filename))).readlines()

setup(...
      install_requires=_parse_requirements('requirements.txt'),
   ...)

distutils.text_file.TextFileコメントが削除されることに注意してください。また、私の経験では、要件ファイルにバンドルするために特別な手順を実行する必要はないようです。


2

行動にparse_requirements注意してください!

pip.req.parse_requirementsアンダースコアがダッシュに変わることに注意してください。これは私がそれを発見する前の数日間私を激怒させました。デモの例:

from pip.req import parse_requirements  # tested with v.1.4.1

reqs = '''
example_with_underscores
example-with-dashes
'''

with open('requirements.txt', 'w') as f:
    f.write(reqs)

req_deps = parse_requirements('requirements.txt')
result = [str(ir.req) for ir in req_deps if ir.req is not None]
print result

作り出す

['example-with-underscores', 'example-with-dashes']

1
使用unsafe_nameをアンダースコアのバージョンを取得する:[ir.req.unsafe_name for ir in req_deps if ir.req is not None]
alanjds

5
他で指摘されているように、PIPはライブラリではなくアプリケーションです。公に合意されたAPIはなく、コードへのインポートはサポートされているユースケースではありません。予期しない動作が発生するのは当然のことです。その内部関数は、このように使用することを意図したものではありません。
Jonathan Hanson、

1

これのために再利用可能な関数を作成しました。実際には、要件ファイルのディレクトリ全体を解析し、それらをextras_requireに設定します。

最新版はいつでもこちらから入手できます:https : //gist.github.com/akatrevorjay/293c26fefa24a7b812f5

import glob
import itertools
import os

# This is getting ridiculous
try:
    from pip._internal.req import parse_requirements
    from pip._internal.network.session import PipSession
except ImportError:
    try:
        from pip._internal.req import parse_requirements
        from pip._internal.download import PipSession
    except ImportError:
        from pip.req import parse_requirements
        from pip.download import PipSession


def setup_requirements(
        patterns=[
            'requirements.txt', 'requirements/*.txt', 'requirements/*.pip'
        ],
        combine=True):
    """
    Parse a glob of requirements and return a dictionary of setup() options.
    Create a dictionary that holds your options to setup() and update it using this.
    Pass that as kwargs into setup(), viola

    Any files that are not a standard option name (ie install, tests, setup) are added to extras_require with their
    basename minus ext. An extra key is added to extras_require: 'all', that contains all distinct reqs combined.

    Keep in mind all literally contains `all` packages in your extras.
    This means if you have conflicting packages across your extras, then you're going to have a bad time.
    (don't use all in these cases.)

    If you're running this for a Docker build, set `combine=True`.
    This will set `install_requires` to all distinct reqs combined.

    Example:

    >>> import setuptools
    >>> _conf = dict(
    ...     name='mainline',
    ...     version='0.0.1',
    ...     description='Mainline',
    ...     author='Trevor Joynson <github@trevor.joynson,io>',
    ...     url='https://trevor.joynson.io',
    ...     namespace_packages=['mainline'],
    ...     packages=setuptools.find_packages(),
    ...     zip_safe=False,
    ...     include_package_data=True,
    ... )
    >>> _conf.update(setup_requirements())
    >>> # setuptools.setup(**_conf)

    :param str pattern: Glob pattern to find requirements files
    :param bool combine: Set True to set install_requires to extras_require['all']
    :return dict: Dictionary of parsed setup() options
    """
    session = PipSession()

    # Handle setuptools insanity
    key_map = {
        'requirements': 'install_requires',
        'install': 'install_requires',
        'tests': 'tests_require',
        'setup': 'setup_requires',
    }
    ret = {v: set() for v in key_map.values()}
    extras = ret['extras_require'] = {}
    all_reqs = set()

    files = [glob.glob(pat) for pat in patterns]
    files = itertools.chain(*files)

    for full_fn in files:
        # Parse
        reqs = {
            str(r.req)
            for r in parse_requirements(full_fn, session=session)
            # Must match env marker, eg:
            #   yarl ; python_version >= '3.0'
            if r.match_markers()
        }
        all_reqs.update(reqs)

        # Add in the right section
        fn = os.path.basename(full_fn)
        barefn, _ = os.path.splitext(fn)
        key = key_map.get(barefn)

        if key:
            ret[key].update(reqs)
            extras[key] = reqs

        extras[barefn] = reqs

    if 'all' not in extras:
        extras['all'] = list(all_reqs)

    if combine:
        extras['install'] = ret['install_requires']
        ret['install_requires'] = list(all_reqs)

    def _listify(dikt):
        ret = {}

        for k, v in dikt.items():
            if isinstance(v, set):
                v = list(v)
            elif isinstance(v, dict):
                v = _listify(v)
            ret[k] = v

        return ret

    ret = _listify(ret)

    return ret


__all__ = ['setup_requirements']

if __name__ == '__main__':
    reqs = setup_requirements()
    print(reqs)

非常に素晴らしい!最新の
PIPで

@amohrありがとう!私は最近それをさらに後のpipのために更新しました、彼らが物事をpip._internal..に移動することによって彼らが彼らのように振る舞っている理由がわかりません。使用可能な外部APIを提供しないなら、あなたはそれらすべてを壊すべきではありませんあなたが提供するすべてを使用しています。
trevorj 2018

0

別の可能な解決策...

def gather_requirements(top_path=None):
    """Captures requirements from repo.

    Expected file format is: requirements[-_]<optional-extras>.txt

    For example:

        pip install -e .[foo]

    Would require:

        requirements-foo.txt

        or

        requirements_foo.txt

    """
    from pip.download import PipSession
    from pip.req import parse_requirements
    import re

    session = PipSession()
    top_path = top_path or os.path.realpath(os.getcwd())
    extras = {}
    for filepath in tree(top_path):
        filename = os.path.basename(filepath)
        basename, ext = os.path.splitext(filename)
        if ext == '.txt' and basename.startswith('requirements'):
            if filename == 'requirements.txt':
                extra_name = 'requirements'
            else:
                _, extra_name = re.split(r'[-_]', basename, 1)
            if extra_name:
                reqs = [str(ir.req) for ir in parse_requirements(filepath, session=session)]
                extras.setdefault(extra_name, []).extend(reqs)
    all_reqs = set()
    for key, values in extras.items():
        all_reqs.update(values)
    extras['all'] = list(all_reqs)
    return extras

そして使用する...

reqs = gather_requirements()
install_reqs = reqs.pop('requirements', [])
test_reqs = reqs.pop('test', [])
...
setup(
    ...
    'install_requires': install_reqs,
    'test_requires': test_reqs,
    'extras_require': reqs,
    ...
)

どこtreeから来たの?
Francesco Boi

@FrancescoBoi完全に機能するソリューションを提示しなかったことを少し許してください...ツリーは、実際にはローカルファイルシステムのスキャンにすぎません(Linuxの「ツリー」コマンドに非常に似ています)。また、pipは常に更新されており、pipの内部を使用しているため、上記の解決策は現時点では完全に機能しない可能性があります。
ブライアンブルージュマン

0

そんなことはお勧めしません。複数回で述べたようにinstall_requiresしてrequirements.txt、間違いなく同じリストすることになっていません。しかし、pipのプライベート内部APIに関連する誤解を招く答えがたくさんあるため、、より健全な代替案を検討する価値があります...

setuptoolsスクリプトからファイルを解析するためにpipは必要ありません。setuptoolsのは、すでにプロジェクトの中で必要なすべてのツールが含まれているトップレベルのパッケージ。requirements.txt setup.pypkg_resources

それは多かれ少なかれこのように見えるかもしれません:

#!/usr/bin/env python3

import pathlib

import pkg_resources
import setuptools

with pathlib.Path('requirements.txt').open() as requirements_txt:
    install_requires = [
        str(requirement)
        for requirement
        in pkg_resources.parse_requirements(requirements_txt)
    ]

setuptools.setup(
    install_requires=install_requires,
)

あなたが知らなかった場合のために、2015年以前から多くの(私も含めて)がpipの解析を使用しており、を使用していない理由pkg_resourcesは、github.com/pypa/setuptools/issues/470などのバグです。この正確なものは現在修正されていますが、両方の実装が別々に開発されているように見えるので、それを使用するのはまだ少し怖いです。
trevorj

@trevorjこれを指摘してくれてありがとう、知らなかった。事実は今日では機能しており、pipを使用することは(特にこの方法で)ばかげたアイデアのように思えます。他の答えを見てみましょう。ほとんどは警告の通知がほとんどなく、同じ不適切なアイデアのわずかなバリエーションのように見えます。そして、初心者はこの傾向に従うだけかもしれません。PEP517やPEP518などのイニシアチブがコミュニティをこの狂気から遠ざけることを期待しています。
sinoroc

-1

このSO質問からの私の回答をクロスポストして、別の単純なpipバージョンの証明ソリューションを求めます。

try:  # for pip >= 10
    from pip._internal.req import parse_requirements
    from pip._internal.download import PipSession
except ImportError:  # for pip <= 9.0.3
    from pip.req import parse_requirements
    from pip.download import PipSession

requirements = parse_requirements(os.path.join(os.path.dirname(__file__), 'requirements.txt'), session=PipSession())

if __name__ == '__main__':
    setup(
        ...
        install_requires=[str(requirement.req) for requirement in requirements],
        ...
    )

次にrequirements.txt、プロジェクトのルートディレクトリの下にすべての要件を投入します。


-1

これは私がしました:

import re

def requirements(filename):
    with open(filename) as f:
        ll = f.read().splitlines()
    d = {}
    for l in ll:
        k, v = re.split(r'==|>=', l)
        d[k] = v
    return d

def packageInfo():
    try:
        from pip._internal.operations import freeze
    except ImportError:
        from pip.operations import freeze

    d = {}
    for kv in freeze.freeze():
        k, v = re.split(r'==|>=', kv)
        d[k] = v
    return d

req = getpackver('requirements.txt')
pkginfo = packageInfo()

for k, v in req.items():
    print(f'{k:<16}: {v:<6} -> {pkginfo[k]}')

-2

さらに別のparse_requirementsハックで、環境マーカーも解析されextras_requireます。

from collections import defaultdict
from pip.req import parse_requirements

requirements = []
extras = defaultdict(list)
for r in parse_requirements('requirements.txt', session='hack'):
    if r.markers:
        extras[':' + str(r.markers)].append(str(r.req))
    else:
        requirements.append(str(r.req))

setup(
    ...,
    install_requires=requirements,
    extras_require=extras
)

sdistとbinary distsの両方をサポートする必要があります。

他の人が述べたように、parse_requirementsいくつかの欠点があるので、これはあなたが公共プロジェクトでやるべきことではありませんが、内部/個人プロジェクトでは十分かもしれません。


pip 20.1でAPIが変更され、マーカーを使用できparse_requirements()なくなったため、失敗しました。
Tuukka Mustonen

-3

以下は、Romainの回答にpip 9.0.1基づく完全なハック(でテスト済み)であり、現在の環境マーカーに従って解析およびフィルタリングしますrequirements.txt

from pip.req import parse_requirements

requirements = []
for r in parse_requirements('requirements.txt', session='hack'):
    # check markers, such as
    #
    #     rope_py3k    ; python_version >= '3.0'
    #
    if r.match_markers():
        requirements.append(str(r.req))

print(requirements)

1
これは部分的にのみ当てはまります。あなたが呼び出すr.match_markers()場合、あなたは実際にマーカーを評価している、それはsdistのために行う正しいことです。ただし、バイナリdist(ホイールなど)をビルドする場合、パッケージにはビルド時の環境に一致するライブラリのみがリストされます。
Tuukka Mustonen

@TuukkaMustonen、それwheel environmentに対してマーカーを評価するためにこれをどこに見つけるか(人がしようとしていることである場合)?
anatly techtonik 2016

もサポートするはずのstackoverflow.com/a/41172125/165629を参照してくださいbdist_wheel。マーカーは評価せず、に追加するだけextras_requireです。
Tuukka Mustonen 2016
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.