यह सवाल लंबे मुझे तंग करने के लिए वापस आ गया के बाद मैं दो साल पहले इस पर टिप्पणी की! मेरे पास हाल ही में एक ही समस्या थी, और मुझे दस्तावेज बहुत दुर्लभ मिला, क्योंकि मुझे लगता है कि आप में से अधिकांश ने अनुभव किया होगा। इसलिए मैंने setuptools और distutils के स्रोत कोड का थोड़ा सा शोध करने की कोशिश की ताकि यह देखने के लिए कि क्या आपके द्वारा पूछे गए दोनों प्रश्नों के लिए मुझे कम या ज्यादा मानक दृष्टिकोण मिल सकता है या नहीं। पैकेज के निर्माण की प्रक्रिया के दौरान एक टर्मिनल कमांड (यानी, मेरे मामले में make
) को चलाने के लिए कैसे, setuptools का उपयोग कर/distutils:
पहला सवाल आप
प्रश्न # 1 पूछा ?
कई दृष्टिकोण है और उन सभी को जब setup
बुला एक cmdclass
की स्थापना शामिल है। setup
के पैरामीटर cmdclass
आदेश ऐसे नाम हैं जो निर्माण के आधार पर निष्पादित या वितरण की जरूरतों को स्थापित हो जाएगा, और वर्गों है कि (distutils.cmd.Command
आधार वर्ग से विरासत एक पक्ष नोट के रूप में के बीच एक मैपिंग, setuptools.command.Command
वर्ग distutils
से प्राप्त होता है 'Command
वर्ग होना चाहिए ताकि आप जिन चीज़ों ayoon किया की तरह किसी भी कमांड नाम को परिभाषित करने की अनुमति देता है, और फिर इसे विशेष रूप से निष्पादित जब कमांड लाइन से python setup.py --install-option="customcommand"
बुला setuptools
कार्यान्वयन से सीधे प्राप्त कर सकते हैं।
cmdclass
। इस के साथ समस्या यह है कि यह नहीं है मानक आदेश जो pip
के माध्यम से पैकेज स्थापित करने का प्रयास करते समय निष्पादित किया जाएगा यापर कॉल करके। इस तक पहुंचने का मानक तरीका यह जांचना है कि setup
सामान्य आदेश में निष्पादित करने का प्रयास करें और उसके बाद उस विशेष cmdclass
को अधिभारित करें।
setuptools.setup
और distutils.setup
में देख से, setup
आदेशों यह found in the command line, मान देता है जो सिर्फ एक सादे install
है चलेंगे। setuptools.setup
के मामले में, यह परीक्षणों की एक श्रृंखला को ट्रिगर करेगा जो देखेंगे कि distutils.install
कमांड क्लास को एक साधारण कॉल का सहारा लेना है, और यदि ऐसा नहीं होता है, तो यह bdist_egg
चलाने का प्रयास करेगा। बदले में, यह आदेश कई चीजें करता है लेकिन build_clib
, build_py
और/या build_ext
आदेशों को कॉल करने के लिए महत्वपूर्ण रूप से निर्णय लेता है। distutils.install
यदि आवश्यक हो तो build
चलाता है जो build_clib
, build_py
और/या build_ext
चलाता है। इसका मतलब यह है कि चाहे आप setuptools
या distutils
का उपयोग करते हैं, यदि स्रोत से निर्माण करना आवश्यक है, तो build_clib
, build_py
, और/या build_ext
आदेश चलाए जाएंगे, इसलिए ये वे हैं जिन्हें हम cmdclass
के साथ ओवरलोड करना चाहते हैं setup
, सवाल तीन में से कौन सा बन जाता है।
build_py
शुद्ध पायथन पैकेज "निर्माण" के लिए उपयोग किया जाता है, इसलिए हम इसे सुरक्षित रूप से अनदेखा कर सकते हैं।
build_ext
का उपयोग setup
फ़ंक्शन पर कॉल के ext_modules
पैरामीटर के माध्यम से घोषित घोषित एक्सटेंशन मॉड्यूल बनाने के लिए किया जाता है।हम इस वर्ग को ओवरलोड करना चाहते हैं, मुख्य विधि है कि प्रत्येक एक्सटेंशन बनाता build_extension
(या here distutils के लिए)
build_clib
घोषित पुस्तकालयों कि setup
कार्य करने के लिए कॉल की libraries
पैरामीटर के माध्यम से पारित कर रहे हैं बनाने के लिए उपयोग किया जाता है। इस मामले में, हमारे व्युत्पन्न वर्ग के साथ हमें मुख्य विधि को अधिभारित करना चाहिए build_libraries
विधि (heredistutils
के लिए)।
मैं एक उदाहरण पैकेज कि setuptools
build_ext
कमांड का उपयोग करके एक Makefile के माध्यम से एक खिलौना ग स्थिर पुस्तकालय बनाता साझा करेंगे। दृष्टिकोण को build_clib
कमांड का उपयोग करने के लिए अनुकूलित किया जा सकता है, लेकिन आपको build_clib.build_libraries
का स्रोत कोड चेकआउट करना होगा।
setup.py
import os, subprocess
import setuptools
from setuptools.command.build_ext import build_ext
from distutils.errors import DistutilsSetupError
from distutils import log as distutils_logger
extension1 = setuptools.extension.Extension('test_pack_opt.test_ext',
sources = ['test_pack_opt/src/test.c'],
libraries = [':libtestlib.a'],
library_dirs = ['test_pack_opt/lib/'],
)
class specialized_build_ext(build_ext, object):
"""
Specialized builder for testlib library
"""
special_extension = extension1.name
def build_extension(self, ext):
if ext.name!=self.special_extension:
# Handle unspecial extensions with the parent class' method
super(specialized_build_ext, self).build_extension(ext)
else:
# Handle special extension
sources = ext.sources
if sources is None or not isinstance(sources, (list, tuple)):
raise DistutilsSetupError(
"in 'ext_modules' option (extension '%s'), "
"'sources' must be present and must be "
"a list of source filenames" % ext.name)
sources = list(sources)
if len(sources)>1:
sources_path = os.path.commonprefix(sources)
else:
sources_path = os.path.dirname(sources[0])
sources_path = os.path.realpath(sources_path)
if not sources_path.endswith(os.path.sep):
sources_path+= os.path.sep
if not os.path.exists(sources_path) or not os.path.isdir(sources_path):
raise DistutilsSetupError(
"in 'extensions' option (extension '%s'), "
"the supplied 'sources' base dir "
"must exist" % ext.name)
output_dir = os.path.realpath(os.path.join(sources_path,'..','lib'))
if not os.path.exists(output_dir):
os.makedirs(output_dir)
output_lib = 'libtestlib.a'
distutils_logger.info('Will execute the following command in with subprocess.Popen: \n{0}'.format(
'make static && mv {0} {1}'.format(output_lib, os.path.join(output_dir, output_lib))))
make_process = subprocess.Popen('make static && mv {0} {1}'.format(output_lib, os.path.join(output_dir, output_lib)),
cwd=sources_path,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
shell=True)
stdout, stderr = make_process.communicate()
distutils_logger.debug(stdout)
if stderr:
raise DistutilsSetupError('An ERROR occured while running the '
'Makefile for the {0} library. '
'Error status: {1}'.format(output_lib, stderr))
# After making the library build the c library's python interface with the parent build_extension method
super(specialized_build_ext, self).build_extension(ext)
setuptools.setup(name = 'tester',
version = '1.0',
ext_modules = [extension1],
packages = ['test_pack', 'test_pack_opt'],
cmdclass = {'build_ext': specialized_build_ext},
)
test_pack/__ init__.py
from __future__ import absolute_import, print_function
def py_test_fun():
print('Hello from python test_fun')
try:
from test_pack_opt.test_ext import test_fun as c_test_fun
test_fun = c_test_fun
except ImportError:
test_fun = py_test_fun
test_pack_opt/__ init__.py
from __future__ import absolute_import, print_function
import test_pack_opt.test_ext
test_pack_opt/src/Makefile
LIBS = testlib.so testlib.a
SRCS = testlib.c
OBJS = testlib.o
CFLAGS = -O3 -fPIC
CC = gcc
LD = gcc
LDFLAGS =
all: shared static
shared: libtestlib.so
static: libtestlib.a
libtestlib.so: $(OBJS)
$(LD) -pthread -shared $(OBJS) $(LDFLAGS) -o [email protected]
libtestlib.a: $(OBJS)
ar crs [email protected] $(OBJS) $(LDFLAGS)
clean: cleantemp
rm -f $(LIBS)
cleantemp:
rm -f $(OBJS) *.mod
.SUFFIXES: $(SUFFIXES) .c
%.o:%.c
$(CC) $(CFLAGS) -c $<
test_pack_opt/src/test.c
#include <Python.h>
#include "testlib.h"
static PyObject*
test_ext_mod_test_fun(PyObject* self, PyObject* args, PyObject* keywds){
testlib_fun();
return Py_None;
}
static PyMethodDef TestExtMethods[] = {
{"test_fun", (PyCFunction) test_ext_mod_test_fun, METH_VARARGS | METH_KEYWORDS, "Calls function in shared library"},
{NULL, NULL, 0, NULL}
};
#if PY_VERSION_HEX >= 0x03000000
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"test_ext",
NULL,
-1,
TestExtMethods,
NULL,
NULL,
NULL,
NULL
};
PyMODINIT_FUNC
PyInit_test_ext(void)
{
PyObject *m = PyModule_Create(&moduledef);
if (!m) {
return NULL;
}
return m;
}
#else
PyMODINIT_FUNC
inittest_ext(void)
{
PyObject *m = Py_InitModule("test_ext", TestExtMethods);
if (m == NULL)
{
return;
}
}
#endif
test_pack_opt/src/testlib.c
#include "testlib.h"
void testlib_fun(void){
printf("Hello from testlib_fun!\n");
}
test_pack_opt/src/testlib.h
#ifndef TESTLIB_H
#define TESTLIB_H
#include <stdio.h>
void testlib_fun(void);
#endif
इस उदाहरण में, ग पुस्तकालय है कि मैं कस्टम Makefile सिर्फ एक समारोह जो stdout में "Hello from testlib_fun!\n"
प्रिंट है का उपयोग करते हुए निर्माण करना चाहते हैं। test.c
स्क्रिप्ट पाइथन और इस लाइब्रेरी के एकल फ़ंक्शन के बीच एक साधारण इंटरफ़ेस है। विचार यह है कि मैं setup
बताता हूं कि मैं test_pack_opt.test_ext
नामक एक सी एक्सटेंशन बनाना चाहता हूं, जिसमें केवल एक स्रोत फ़ाइल है: test.c
इंटरफ़ेस स्क्रिप्ट, और मैं यह भी विस्तार बताता हूं कि इसे स्थिर पुस्तकालय libtestlib.a
के विरुद्ध लिंक करना होगा। मुख्य बात यह है कि मैं specialized_build_ext(build_ext, object)
का उपयोग कर build_ext
cmdclass को अधिभारित करता हूं। object
से विरासत केवल तभी जरूरी है जब आप पेरेंट क्लास विधियों को प्रेषित करने के लिए super
पर कॉल करने में सक्षम होना चाहते हैं। build_extension
विधि Extension
उदाहरण दूसरे Extension
उदाहरणों के साथ अच्छा काम करने के लिए build_extension
के डिफ़ॉल्ट व्यवहार की आवश्यकता के लिए Extension
उदाहरण लेता है, मैं जांचता हूं कि इस एक्सटेंशन का विशेष नाम है या नहीं और यदि मैं इसे कॉल नहीं करता super
की build_extension
विधि।
विशेष पुस्तकालय के लिए, मैं मेकफ़ाइल को केवल subprocess.Popen('make static ...'). The rest of the command passed to the shell is just to move the static library to a certain default location in which the library should be found to be able to link it to the rest of the compiled extension (which is also just compiled using the
सुपर 's
build_extension` विधि के साथ कॉल करता हूं)।
जैसा कि आप कल्पना कर सकते हैं कि इस कोड को अलग-अलग व्यवस्थित करने के कई तरीके हैं, इसलिए उन्हें सभी को सूचीबद्ध करने का अर्थ नहीं है। मुझे उम्मीद है कि यह उदाहरण मेकफ़ाइल को कॉल करने के तरीके को दिखाता है, और cmdclass
और Command
व्युत्पन्न कक्षा को आपको मानक स्थापना में make
पर कॉल करने के लिए ओवरलोड करना चाहिए।
अब, सवाल पर 2.
प्रश्न # 2: कैसे सुनिश्चित करने के लिए, केवल कि इस तरह के टर्मिनल कमांड निष्पादित किया जाता है, तो इसी extra1 स्थापना प्रक्रिया के दौरान निर्दिष्ट किया जाता है?
यह के features
पैरामीटर के साथ संभव था। मानक तरीका है कि आवश्यकताओं को पूरा करने के आधार पर पैकेज स्थापित करने का प्रयास करें। install_requires
अनिवार्य आवश्यकताओं को सूचीबद्ध करता है, extras_requires
वैकल्पिक आवश्यकताओं को सूचीबद्ध करता है। pip install Project-A[PDF]
फोन करके setuptools
documentation
setup(
name="Project-A",
...
extras_require={
'PDF': ["ReportLab>=1.2", "RXP"],
'reST': ["docutils>=0.3"],
}
)
आप वैकल्पिक आवश्यक संकुल की स्थापना के लिए मजबूर कर सकता है से उदाहरण के लिए, लेकिन अगर किसी कारण से 'PDF'
अतिरिक्त नामित के लिए आवश्यकताओं को पहले से संतुष्ट थे, pip install Project-A
उसी के साथ खत्म होगा "Project-A"
कार्यक्षमता। इसका अर्थ यह है कि जिस तरह से "प्रोजेक्ट-ए" स्थापित किया गया है, कमांड लाइन पर निर्दिष्ट प्रत्येक अतिरिक्त के लिए अनुकूलित नहीं किया गया है, "प्रोजेक्ट-ए" हमेशा उसी तरह स्थापित करने का प्रयास करेगा और अनुपलब्ध होने के कारण कम कार्यक्षमता के साथ समाप्त हो सकता है वैकल्पिक आवश्यकताओं।
जो मैंने समझा, उससे इसका मतलब है कि आपके मॉड्यूल एक्स को संकलित और स्थापित करने के लिए केवल [अतिरिक्त 1] निर्दिष्ट किया गया है, तो आपको मॉड्यूल एक्स को एक अलग पैकेज के रूप में शिप करना चाहिए और extras_require
के माध्यम से उस पर निर्भर होना चाहिए। कल्पना मॉड्यूल एक्स my_package_opt
में भेज दिया जाएगा चलें, my_package
के लिए अपने स्थापना दिखना चाहिए
तरह
setup(
name="my_package",
...
extras_require={
'extra1': ["my_package_opt"],
}
)
ठीक है, मुझे खेद है कि मेरा उत्तर इतने लंबे समय से किया जा रहा समाप्त हो गया हूँ, लेकिन मुझे आशा है कि यह मदद करता है। किसी भी वैचारिक या नामकरण त्रुटि को इंगित करने में संकोच न करें, क्योंकि मैंने ज्यादातर इसे setuptools
स्रोत कोड से निकालने का प्रयास किया था।
सकारात्मक डुप्लिकेट [मैं setup.py में मेकफ़ाइल कैसे चला सकता हूं] (http://stackoverflow.com/questions/1754966/how-can-i-run-a-makefile-in-setup-py)? – lucianopaz
बिल्कुल नहीं। यह ए) किसी स्थिति के लिए उत्तर नहीं है, जब ऐसी स्थापना की आवश्यकता होती है, केवल तभी जब "अतिरिक्त 1" शामिल होता है। बी) यह वास्तव में जानकारीपूर्ण/विस्तृत नहीं है। मैं एक और विस्तृत उत्तर की सराहना करता हूं, और मेरा मानना है कि समुदाय के लिए यह बहुत ही जानकारीपूर्ण होगा यदि विस्तृत विवरण प्रदान किया गया हो। –
क्या 'X' में 'setup.py' है और इस प्रकार एक नियमित पायथन पैकेज है? – fpbhb