सेफ़टम्स setup.txt फ़ाइल में install_requires kwarg के लिए संदर्भ आवश्यकताएँ


279

मेरे पास एक requirements.txtफाइल है जिसे मैं ट्रैविस-सीआई के साथ उपयोग कर रहा हूं। यह मूर्खतापूर्ण लगता है दोनों में आवश्यकताओं की नकल करने के लिए 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इसका उपयोग वातावरण की स्थापना को स्वचालित करने के लिए किया जाता है, जो अतिरिक्त सॉफ़्टवेयर स्थापित करने और संस्करण को पिन करने की अनुमति देता है और इसका उपयोग sysadminsing द्वारा किया जाता है पैकेज। उनकी भूमिका और लक्षित दर्शक काफी भिन्न होते हैं, इसलिए उन्हें ओपी इच्छाओं की तरह संयोजित करने की कोशिश करना एक वास्तविक डिजाइन की गलती है।
ज़ार्ट

7
मेरे 2 सेंट। अपने setup.py में आवश्यकताओं का उपयोग न करें। उद्देश्य अलग हैं, ared caremad.io/2013/07/setup-vs-requirement
फिलिप ओम्ब्रेडैनने

3
मुझे बहुत से जटिल उत्तर दिखाई देते हैं। सादे पुराने के साथ क्या गलत है [line.strip() for line in open("requirements.txt").readlines()]?
फेलिप एसएस श्नाइडर

ऐसा करने के लिए अनुशंसित नहीं है। लेकिन अगर वास्तव में जरूरत है तो यह सीधा है: स्वप्नदोष अपने आप में पहले से ही सब कुछ आवश्यक हैpkg_resources.parse_requirements()
पापरोक

जवाबों:


246

आप इसके चारों ओर फ्लिप कर सकते हैं और इसमें निर्भरताएं सूचीबद्ध कर सकते हैं और इसके बजाय setup.pyएक एकल वर्ण - एक बिंदु .- कर सकते requirements.txtहैं।


वैकल्पिक रूप से, भले ही सलाह न दी गई हो, फिर भी requirements.txtफ़ाइल को पार्स करना संभव है (यदि यह URL द्वारा किसी भी बाहरी आवश्यकताओं को संदर्भित नहीं करता है) निम्न हैक के साथ (परीक्षण के साथ pip 9.0.1):

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

हालांकि यह पर्यावरण मार्करों को फ़िल्टर नहीं करता है ।


पाइप के पुराने संस्करणों में, विशेष रूप से 6.0 से अधिक पुराने , एक सार्वजनिक एपीआई है जिसका उपयोग इसे प्राप्त करने के लिए किया जा सकता है। एक आवश्यकता फ़ाइल में टिप्पणियाँ हो सकती हैं ( #) और कुछ अन्य फ़ाइलें ( --requirementया -r) शामिल हो सकती हैं । इस प्रकार, यदि आप वास्तव में पार्स करना चाहते हैं तो आप requirements.txtपाइप पार्सर का उपयोग कर सकते हैं:

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
यदि उपयोगकर्ता के पास पाइप स्थापित नहीं है तो क्या होगा? केए उछाल?
ग्रिंगो सुवे

82
@GringoSuave यदि उपयोगकर्ता के पास पाइप स्थापित नहीं है, तो उसे पहले इसे स्थापित करने की आवश्यकता है।
गुप्ताली

7
आपको अपनी आवश्यकताओं की फ़ाइल में भी उरल्स की आपूर्ति करने की आवश्यकता है, अगर कोई -ई या -f ("संपादन योग्य" गिट रेपो) लाइनें हैं जो गैर-पीपीआई पैकेजों की ओर इशारा करती हैं। इस का उपयोग करें:setup(..., dependency_links=[str(req_line.url) for req_line in parse_requirements(<requirements_path>)], ...)
hobs

91
आप वास्तव में ऐसा नहीं करना चाहते हैं। पाइप मेंटेनर पाइप के रूप में बोलते हुए एक एपीआई के रूप में बुलाया जा रहा है, बिल्कुल भी समर्थन नहीं करता है। वास्तव में पाइप 1.6 (इस समय अगला संस्करण) इस फ़ंक्शन को आगे बढ़ाता है।
डोनाल्ड स्टफट

26
यह अब स्वीकृत उत्तर नहीं होना चाहिए, अगर यह कभी होना चाहिए। यह स्पष्ट रूप से टूट गया है। यहां तक ​​कि जब यह काम किया, यह स्पष्ट रूप से अनावश्यक है। चूंकि pipसे निर्भरता पार्स करने के लिए डिफ़ॉल्ट setup.pyके अभाव में requirements.txt, इसका सरल उत्तर astutely द्वारा नोट टोबू नीचे करने के लिए है में सभी निर्भरता सूची setup.pyऔर निकालें requirements.txtदोनों की आवश्यकता वाले अनुप्रयोगों के लिए, केवल चरित्र requirements.txtतक निर्भरता सूची को कम करें .किया हुआ।
सेसिल करी

194

इसके चेहरे पर, ऐसा लगता है कि requirements.txtऔर setup.pyमूर्खतापूर्ण डुप्लिकेट हैं, लेकिन यह समझना महत्वपूर्ण है कि जबकि रूप समान है, इच्छित फ़ंक्शन बहुत अलग है।

एक पैकेज लेखक का लक्ष्य, जब निर्भरता को निर्दिष्ट करता है, तो यह कहना है कि "जहां भी आप इस पैकेज को स्थापित करते हैं, ये अन्य पैकेज हैं जिनकी आपको जरूरत है, ताकि इस पैकेज के लिए काम किया जा सके।"

इसके विपरीत, परिनियोजन लेखक (जो एक ही समय में एक ही व्यक्ति हो सकता है) के पास एक अलग काम है, इसमें वे कहते हैं "यहां उन पैकेजों की सूची है जिन्हें हमने एक साथ इकट्ठा किया है और परीक्षण किया है और जिन्हें अब मुझे स्थापित करने की आवश्यकता है"।

पैकेज लेखक विभिन्न प्रकार के परिदृश्यों के लिए लिखता है, क्योंकि वे अपने काम को उन तरीकों से उपयोग करने के लिए डाल रहे हैं जिनके बारे में वे नहीं जानते हैं, और यह जानने का कोई तरीका नहीं है कि उनके पैकेज के साथ क्या पैकेज स्थापित किए जाएंगे। एक अच्छा पड़ोसी होने और अन्य पैकेजों के साथ निर्भरता संस्करण संघर्ष से बचने के लिए, उन्हें निर्भरता संस्करणों की एक विस्तृत श्रृंखला के रूप में निर्दिष्ट करने की आवश्यकता है जो संभवतः काम कर सकते हैं। यह क्या है install_requiresमें setup.pyहै।

तैनाती लेखक एक बहुत ही अलग, बहुत विशिष्ट लक्ष्य के लिए लिखता है: एक स्थापित एप्लिकेशन या सेवा का एक एकल उदाहरण, किसी विशेष कंप्यूटर पर स्थापित। किसी परिनियोजन को ठीक से नियंत्रित करने के लिए, और सुनिश्चित करें कि सही संकुल का परीक्षण किया और तैनात किया गया है, परिनियोजन लेखक को स्थापित करने के लिए हर पैकेज का सटीक संस्करण और स्रोत-स्थान निर्दिष्ट करना होगा, जिसमें निर्भरता और निर्भरता निर्भरता शामिल है। इस कल्पना के साथ, एक तैनाती को कई मशीनों पर बार-बार लागू किया जा सकता है, या एक परीक्षण मशीन पर परीक्षण किया जा सकता है, और तैनाती लेखक को भरोसा हो सकता है कि हर बार एक ही पैकेज तैनात किया जाता है। यह क्या requirements.txtकरता है।

तो आप यह देख सकते हैं, जबकि वे दोनों पैकेज और संस्करणों की एक बड़ी सूची की तरह दिखते हैं, इन दोनों चीजों में बहुत अलग काम हैं। और निश्चित रूप से यह मिश्रण करना और इसे गलत करना आसान है! लेकिन इस बारे में सोचने का सही तरीका यह है कि requirements.txtसभी विभिन्न setup.pyपैकेज फाइलों में आवश्यकताओं के अनुसार "प्रश्न" का "उत्तर" है । इसे हाथ से लिखने के बजाय, यह अक्सर setup.pyवांछित पैकेजों के सेट में सभी फाइलों को देखने के लिए पाइप को बताकर उत्पन्न होता है , ऐसे पैकेजों का एक सेट खोजें, जो यह सोचते हैं कि सभी आवश्यकताओं को फिट बैठता है, और फिर, उन्हें स्थापित करने के बाद, "फ्रीज करें "एक पाठ फ़ाइल में संकुल की सूची (यह वह जगह है जहाँ से pip freezeनाम आता है)।

तो takeaway:

  • setup.pyअभी भी काम कर रहे हैं जो सबसे कम संभव निर्भरता संस्करणों की घोषणा करनी चाहिए। इसका काम यह कहना है कि कोई विशेष पैकेज किसके साथ काम कर सकता है।
  • requirements.txtएक परिनियोजन प्रकटन है जो संपूर्ण इंस्टॉलेशन कार्य को परिभाषित करता है, और इसे किसी एक पैकेज से बंधा हुआ नहीं माना जाना चाहिए। इसका काम तैनाती काम करने के लिए सभी आवश्यक पैकेजों की एक विस्तृत सूची घोषित करना है।
  • क्योंकि इन दो चीजों में मौजूदा के लिए ऐसी अलग-अलग सामग्री और कारण हैं, बस एक को दूसरे में कॉपी करना संभव नहीं है।

संदर्भ:


10
यह सबसे अच्छा स्पष्टीकरण में से एक है जो मुझे उस गड़बड़ में कुछ आदेश देता है जिसे पैकेज इंस्टॉलेशन कहा जाता है! :)
कौंनवी

6
यह अभी भी मेरे लिए स्पष्ट नहीं है कि एक डेवलपर requirements.txtपैकेज के स्रोत के साथ एक संस्करण-नियंत्रित क्यों रखेगा जिसमें स्थापना या परीक्षण के लिए ठोस / स्थिर आवश्यकताएं हैं। निश्चित रूप से setup.pyइस उद्देश्य के लिए परियोजना के भीतर ही इस्तेमाल किया जा सकता है? मैं केवल परियोजना के प्रबंधन के लिए उपयोग किए जाने वाले उपकरणों के लिए इस तरह की फ़ाइल का उपयोग करने की कल्पना कर सकता हूं (उदाहरण के लिए रीफ़ैक्टरिंग, रिलीज़ करना आदि)।
सैम ब्राइटमैन

2
@samBrightman मैं पूरी तरह सहमत हूं, मुझे नहीं लगता कि लाइब्रेरी पैकेज या एप्लिकेशन पैकेजों को कोड के साथ रिपॉजिटरी में अपनी आवश्यकताओं को पूरा करना चाहिए। मुझे लगता है कि निर्माण परीक्षण के दौरान उत्पन्न एक कलाकृति होनी चाहिए, और फिर एक निर्माण प्रकटन का दस्तावेजीकरण करने और अंततः एक तैनाती विरूपण साक्ष्य उत्पन्न करने के लिए उपयोग किया जाना चाहिए।
जोनाथन हैनसन

6
तो आप कह रहे हैं कि requirements.txtदुनिया की स्थिति के लिए और अधिक प्रलेखन है जो किसी दिए गए निर्माण का उत्पादन करता है, भले ही इसका उपयोग आमतौर पर निर्माण प्रक्रिया में ही नहीं किया जाता है? यह समझ आता है। हालाँकि, ऐसा लगता है कि कई सिस्टम दोहराव पर निर्भर हैं: ट्रैविस आपके virtualenv में कुछ डिफ़ॉल्ट (पुराने) पैकेजों को स्थापित करता है और उपयोग करने के लिए कहता है requirements.txt। अगर मैं पूछता हूं कि नवीनतम उपयोग पर निर्भरता कैसे सुनिश्चित करें setup.py, तो लोग जोर देते हैं कि मुझे उपयोग करना चाहिए requirements.txt
सैम ब्राइटमैन

2
इनमें से किसी एक से निकलने की सबसे अच्छी सलाह यह है कि एक ऐसा मॉडल ढूंढा जाए जो आपके लिए काम करे, उसे अच्छी तरह से प्रलेखित करे, और सुनिश्चित करें कि आप जिस किसी के साथ भी काम करते हैं, उसे समझें। यह सोचें कि आप प्रत्येक बिट क्यों कर रहे हैं और क्या यह वास्तव में आपके उपयोग के मामले के लिए समझ में आता है। और पाइथन में बिल्डिंग, पैकेजिंग और पब्लिशिंग की वर्तमान स्थिति के बारे में जितना हो सके उतना बेहतर तरीके से पढ़ने की कोशिश करें, अगर चीजें बेहतर हों। लेकिन अपनी सांस को रोककर न रखें।
जोनाथन हैनसन

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। हालाँकि, यह काम नहीं करता है यदि आप घोषणात्मक वाक्यविन्यास का उपयोग नहीं करते हैं।
पायोत्र डोब्रोगोस्ट

55
@PiotrDobrogost शायद PyCharm डेवलपर को अपने कार्यक्रम को ठीक करना चाहिए। setup.pyएक प्रोग्राम है जिसे चलाया जाना चाहिए, न कि एक डेटा फ़ाइल जिसे पार्स किया जाना चाहिए। यह इस जवाब को किसी भी बदतर नहीं बनाता है।
फ्रेड्रिक ब्रेनन

5
मैं सिर्फ संभावित समस्याओं की ओर इशारा कर रहा हूं; यह उत्तर पूरी तरह से ठीक है। यह केवल PyCharm नहीं है जिसके पास कोड के पीछे "छिपी" जानकारी होने की समस्या है। यह सार्वभौमिक समस्या है और इस प्रकार पायथन पैकेजिंग में मेटाडेटा की घोषणा विनिर्देश की ओर सामान्य कदम है।
पायोत्र डोब्रोगोस्ट

32
जब तक आप अपने include requirements.txtमें डालते हैं तब तक ठीक काम करता है MANIFEST.inया आप स्रोत वितरण से अपने पुस्तकालय को स्थापित करने में सक्षम नहीं होंगे।
पंकरत

4
मुझे पता है कि यह एक पुराना प्रश्न है, लेकिन आप कम से कम आजकल PyCharm को प्राथमिकताएं-> टूल्स-> टूल्स-> पाइथन इंटीग्रेटेड टूल्स-> पैकेज रिक्वायरमेंट्स फाइल
lekksi

64

आवश्यकताएँ फाइलें एक विस्तारित पाइप प्रारूप का उपयोग करती हैं, जो केवल तभी उपयोगी होती है जब आपको अपने setup.pyमजबूत अवरोधों के साथ पूरक करने की आवश्यकता होती है , उदाहरण के लिए सटीक यूआरएल को निर्दिष्ट करने से कुछ निर्भरताएं आनी चाहिए, या pip freezeज्ञात-कार्य करने के लिए सेट किए गए पूरे पैकेज को फ्रीज करने के लिए आउटपुट संस्करणों। यदि आपको अतिरिक्त बाधाओं की आवश्यकता नहीं है, तो केवल a का उपयोग करें setup.py। यदि आपको लगता है कि आपको वास्तव में किसी requirements.txtभी तरह से जहाज करने की आवश्यकता है , तो आप इसे एक पंक्ति बना सकते हैं:

.

यह मान्य होगा और उसी की सामग्री setup.pyको उसी निर्देशिका में संदर्भित करेगा ।


9
लेकिन इस मामले में यह मेरे ऐप को भी स्थापित करने की कोशिश करेगा। क्या होगा अगर मुझे इसकी आवश्यकता नहीं है और केवल install_requires स्थापित करना चाहते हैं?
ffeast

2
@Ffeast क्या पूछ रहा है, इसके बारे में विस्तार से बताने के लिए, यदि आवश्यकताएं केवल setup.py में मौजूद हैं, तो क्या pip install -r requirements.txt पैकेज को स्थापित किए बिना आवश्यकताओं (बराबर ) को स्थापित करने का कोई तरीका है?
haridsv

1
@ffeast @haridsv -e .पर्याप्त होना चाहिए। इस पृष्ठ को देखें: caremad.io/posts/2013/07/setup-vs-requirement
dexhunter

4
@ DexD.Hunter यह अभी भी एप्लिकेशन खुद को स्थापित करने की कोशिश करता है। यह वह नहीं है जो हम चाहते हैं
ffeast

38

हालांकि इस सवाल का सटीक उत्तर नहीं है, मैं https://caremad.io/2013/07/setup-vs-requirement/ पर डोनाल्ड स्टफ्ट के ब्लॉग पोस्ट की सलाह देता हूं इस समस्या पर अच्छे विचार के लिए । मैं इसे बड़ी सफलता के लिए उपयोग कर रहा हूं।

संक्षेप में, requirements.txtएक setup.pyविकल्प नहीं है , लेकिन एक तैनाती पूरक है। में पैकेज निर्भरता का एक उपयुक्त अमूर्त रखें setup.py। सेटrequirements.txt या अधिक 'उन्हें विकास, परीक्षण या उत्पादन के लिए पैकेज निर्भरता के विशिष्ट संस्करणों को लाने के लिए।

उदाहरण के लिए रेपो में शामिल पैकेजों के साथ deps/:

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

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

पाइप पैकेज का निष्पादन करता है setup.pyऔर इसमें घोषित निर्भरता के विशिष्ट संस्करणों को स्थापित करता है install_requires। कोई दोहराव नहीं है और दोनों कलाकृतियों का उद्देश्य संरक्षित है।


7
जब आप दूसरों के माध्यम से स्थापित करने के लिए पैकेज प्रदान करना चाहते हैं तो यह काम नहीं करता है pip install my-package। अगर मेरे पैकेज के लिए निर्भरताएँ my-package / setup.py में सूचीबद्ध नहीं हैं, तो वे द्वारा स्थापित नहीं हैं pip install my-package। मैं यह निर्धारित करने में असमर्थ हूं कि दूसरों के लिए एक पैकेज कैसे प्रदान किया जाए जिसमें सेटअपफ्रीडम में स्पष्ट रूप से बताए बिना निर्भरता शामिल है। यह जानना पसंद करेंगे कि क्या किसी ने यह पता लगाया है कि आवश्यकताओं को डाउनलोड करने और मैन्युअल रूप से कॉल करने के बिना दूसरों को मेरा पैकेज + निर्भरता स्थापित करने की अनुमति देते समय इसे DRY कैसे रखा जाए pip install -r my-package/requirements.txt
मालिना

2
@ मालिना यहां पैकेज पूरी तरह से बिना इंस्टॉल किए है requirements.txt। यह पूरी बात है। चीजों को और अधिक स्पष्ट करने के लिए प्रश्न का अद्यतन करें। साथ ही अप्रचलित ब्लॉग पोस्ट लिंक को भी अपडेट किया।
प्रसिद्धगर्किन

इसलिए जब सेटअपहोम चल रहा है, तो यह स्टॉक्सफोरम में सूचीबद्ध फ़ाइलों के विशिष्ट संस्करणों के लिए आवश्यकताओं को बुलाएगा।
16'16

यह @dtracers के आसपास दूसरा तरीका है। आवश्यकताएँ। यह स्वयं के पैकेज की ओर इंगित करता है, जहां सेटअप के भरोसेमंद लोगों को उठाया जा सकता है। इसलिए आवश्यकताओं का उपयोग करते समय, यह काम करता है और जब पाइप के माध्यम से स्थापित होता है, तो यह भी काम करता है - दोनों मामलों में setup.py की निर्भरता का उपयोग करते हुए, लेकिन आवश्यकताओं का उपयोग करते समय अधिक चीजें स्थापित करने की अनुमति भी देता है
।xt

20

उपयोग करना parse_requirementsसमस्याग्रस्त है क्योंकि पाइप एपीआई सार्वजनिक रूप से प्रलेखित और समर्थित नहीं है। पाइप 1.6 में, वह फ़ंक्शन वास्तव में चल रहा है, इसलिए इसके मौजूदा उपयोग के टूटने की संभावना है।

के बीच दोहराव को खत्म करने का एक और विश्वसनीय तरीका है setup.pyऔर requirements.txtअपनी निर्भरता को विशिष्ट बनाना setup.pyऔर फिर -e .अपनी requirements.txtफाइल में डालना । pipडेवलपर्स में से कुछ के बारे में जानकारी कि क्यों जाने के लिए एक बेहतर तरीका यहाँ उपलब्ध है: https://caremad.io/blog/setup-vs-requirement/


@ टॉमी यह कोशिश करें: caremad.io/2013/07/setup-vs-requirement यह वही लिंक है जो किसी अन्य उत्तर में पोस्ट किया गया है।
अमित

18

ऊपर दिए गए अन्य उत्तर पाइप के एपीआई के वर्तमान संस्करण के साथ काम नहीं करते हैं। यहां पाइप के वर्तमान संस्करण के साथ इसे करने का सही * तरीका है (लेखन के समय 6.0.8, जो 7.1.2 में भी काम करता है। आप अपने संस्करण को पाइप-वी के साथ जांच सकते हैं)।

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

* सही है, इसमें वर्तमान पाइप के साथ parse_requirements का उपयोग करने का तरीका है। यह अभी भी शायद ऐसा करने का सबसे अच्छा तरीका नहीं है, क्योंकि, जैसा कि ऊपर पोस्टर में कहा गया है, पाइप वास्तव में एक एपीआई को बनाए नहीं रखता है।


14

ट्रैविस में वर्तमान पैकेज स्थापित करें। यह एक requirements.txtफ़ाइल के उपयोग से बचा जाता है । उदाहरण के लिए:

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

2
यह अब तक "सही" और "व्यावहारिक" का सबसे अच्छा संयोजन है। मुझे लगता है कि अगर परीक्षण पास होने के बाद आप ट्रैविस को एक आवश्यकताओं को उत्पन्न करने के लिए प्राप्त कर सकते हैं। pip freezeएक कलाकृति के रूप में कहीं और उस फ़ाइल को निर्यात करने के लिए (जैसे S3 या कुछ), तो आपके पास दोहराने के लिए एक शानदार तरीका होगा। का परीक्षण किया।
जोनाथन हैंसन

4

from pip.req import parse_requirements मेरे लिए काम नहीं किया और मुझे लगता है कि यह मेरी आवश्यकताओं में रिक्त लाइनों के लिए है

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

यदि आप अपने उपयोगकर्ताओं को पाइप स्थापित करने के लिए बाध्य नहीं करना चाहते हैं, तो आप इसके साथ अपने व्यवहार का अनुकरण कर सकते हैं:

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

निम्नलिखित इंटरफ़ेस पाइप 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% समय पर काम करता है। पायथन 3.6+ का उपयोग करने वालों के लिए, मैंने एक उत्तरpathlib लिखा है जो इसका एक रूप है।
एक्यूमेनस

3

यह सरल दृष्टिकोण आवश्यकताओं फ़ाइल को पढ़ता है setup.py। यह दिमित्री एस के उत्तर का एक रूपांतर है । यह उत्तर केवल पायथन 3.6+ के साथ संगत है।

प्रति डीएस , 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_requirementsBEHAVIOR से सावधान !

कृपया ध्यान दें कि 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 एक एप्लिकेशन है, लाइब्रेरी नहीं। यह एपीआई पर सार्वजनिक रूप से सहमत नहीं है, और इसे अपने कोड में आयात करना एक समर्थित उपयोग मामला नहीं है। यह आश्चर्य की बात नहीं है कि इसमें अप्रत्याशित व्यवहार है; इसके आंतरिक कार्यों का कभी इस तरह उपयोग करने का इरादा नहीं था।
जोनाथन हैनसन

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)

बहुत अच्छा! यहां तक ​​कि नवीनतम पाइप के साथ पुनरावर्ती आवश्यकताएं भी संभालता है
एमर्र

@amohr धन्यवाद! मैंने हाल ही में इसे एक और बाद के पाइप के लिए भी अपडेट किया है, मैं अनिश्चित हूं कि वे जिस तरह से काम कर रहे हैं, वे चीजों को आगे बढ़ाते हुए pip._internal.. यदि आप एक उपयोगी बाहरी एपीआई प्रदान नहीं करते हैं, तो आपको उन सभी को नहीं तोड़ना चाहिए जो आपके द्वारा प्रदत्त सभी का उपयोग कर रहे हैं।
trevorj

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से आता है
फ्रांसेस्को बोई

@FrancescoBoi यदि आप मुझे पूरी तरह से काम कर रहे समाधान को पेश नहीं करने के लिए थोड़ा माफ करते हैं ... पेड़ वास्तव में स्थानीय फ़ाइल सिस्टम का एक स्कैन है (लिनक्स में "पेड़" कमांड के समान)। इसके अलावा, ऊपर दिया गया मेरा समाधान इस बिंदु पर पूरी तरह से काम नहीं कर सकता है क्योंकि पाइप को लगातार अपडेट किया जा रहा है और मैंने पाइप इंटर्नल्स का उपयोग किया है।
ब्रायन ब्रूगमैन

0

मैं ऐसी बात करने की सलाह नहीं दूंगा। जैसा कि कई बार उल्लेख किया गया है install_requiresऔर requirements.txtनिश्चित रूप से समान सूची नहीं होनी चाहिए। लेकिन चूंकि पाइप के निजी आंतरिक एपीआई को शामिल करने के लिए चारों ओर बहुत सारे भ्रामक उत्तर हैं , इसलिए हो सकता है कि यह वैकल्पिक रूप से देखने लायक हो ...

किसी सेटपूल स्क्रिप्ट से किसी फ़ाइल को पार्स करने के लिए पाइप की आवश्यकता नहीं है । 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 यह इंगित करने के लिए धन्यवाद, मुझे नहीं पता था। तथ्य आजकल यह काम करता है और इसमें शामिल होना मेरे लिए एक हास्यास्पद विचार है (विशेषकर इस फैशन में)। अन्य उत्तरों पर एक नज़र डालें, अधिकांश एक ही बीमार विचार के मामूली बदलावों की तरह प्रतीत होते हैं, बिना किसी चेतावनी नोटिस के। और नए लोग इस प्रवृत्ति का अनुसरण कर सकते हैं। उम्मीद है कि PEP517 और PEP518 जैसी पहल इस पागलपन से समुदाय को दूर करेंगी।
सिनोरोक

-1

एक और सरल, पाइप संस्करण प्रूफ समाधान के लिए इस एसओ प्रश्न से मेरा उत्तर पोस्ट क्रॉस करें

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
)

इसे एसडीस्ट और बाइनरी डिस्ट दोनों का समर्थन करना चाहिए।

जैसा कि दूसरों द्वारा कहा गया है, parse_requirementsकई कमियां हैं, इसलिए यह वह नहीं है जो आपको सार्वजनिक परियोजनाओं पर करना चाहिए, लेकिन यह आंतरिक या व्यक्तिगत परियोजनाओं के लिए पर्याप्त हो सकता है।


पाइप 20.1 ने अपने एपीआई को बदल दिया और मार्कर parse_requirements()अब तक उपलब्ध नहीं हैं, इसलिए यह अब विफल हो जाता है।
टुकेका मस्टोनन

-3

रोमेन के जवाब के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 के लिए सही बात है। हालाँकि, यदि आप बाइनरी डिस्ट (जैसे पहिया) का निर्माण कर रहे हैं, तो पैकेज केवल उन पुस्तकालयों को सूचीबद्ध करेगा जो आपके बिल्ड-टाइम वातावरण से मेल खाते हैं।
तुक्का मुस्टोंनन

@TuukkaMustonen, तो wheel environmentइसके विरुद्ध मार्करों का मूल्यांकन करने के लिए इसे कहाँ से खोजा जाए (अगर यह वह व्यक्ति है जो करने की कोशिश करता है)?
अनातोली टेकटोनिक

देखें stackoverflow.com/a/41172125/165629 जो भी समर्थन करना चाहिए bdist_wheel। यह मार्करों का मूल्यांकन नहीं करता है, यह सिर्फ उन्हें जोड़ता है extras_require
तुक्का मुस्टोंन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.