मुझे पायथन पैकेज की संरचना कैसे करनी चाहिए जिसमें साइथन कोड शामिल है


122

मैं कुछ साइथन कोड वाले पायथन पैकेज बनाना चाहता हूं । मुझे साइथन कोड अच्छी तरह से काम कर रहा है। हालांकि, अब मैं जानना चाहता हूं कि इसे कैसे पैकेज करना है।

ज्यादातर लोग जो केवल पैकेज को स्थापित करना चाहते हैं, मैं उस .cफ़ाइल को शामिल करना चाहता हूं जिसे साइथन बनाता है, और setup.pyमॉड्यूल का उत्पादन करने के लिए संकलन करने की व्यवस्था करता है। तब उपयोगकर्ता को पैकेज स्थापित करने के लिए साइथन को स्थापित करने की आवश्यकता नहीं होती है।

लेकिन उन लोगों के लिए जो पैकेज को संशोधित करना चाहते हैं, मैं साइथॉन .pyxफाइलें भी प्रदान करना चाहूंगा , और किसी तरह setup.pyउन्हें साइथन का उपयोग करके निर्माण करने की अनुमति भी दूंगा (इसलिए उन उपयोगकर्ताओं को साइथन को स्थापित करने की आवश्यकता होगी )।

इन दोनों परिदृश्यों को पूरा करने के लिए मुझे पैकेज में फाइलें कैसे तैयार करनी चाहिए?

Cython प्रलेखन एक छोटे से मार्गदर्शन देता है । लेकिन यह नहीं कहता है कि एक एकल कैसे बनाया जाए जो setup.pyसाइथन मामलों के साथ / बिना दोनों को संभालता है।


1
मैं देख रहा हूं कि प्रश्न किसी भी उत्तर की तुलना में अधिक वोट प्राप्त कर रहा है। मैं यह जानने के लिए उत्सुक हूं कि लोगों को असंतोषजनक उत्तर क्यों मिल सकते हैं।
क्रेग मैकक्विन

4
मुझे दस्तावेज़ीकरण का यह खंड मिला , जो जवाब बिल्कुल देता है।
विल

जवाबों:


72

मैंने खुद इसे अब पायथन पैकेज simplerandom( BitBucket repo - EDIT: now github ) में किया है (मुझे उम्मीद नहीं है कि यह एक लोकप्रिय पैकेज होगा, लेकिन साइथन को सीखने का यह अच्छा मौका था)।

यह विधि इस तथ्य पर निर्भर करती है कि एक .pyxफ़ाइल का निर्माण Cython.Distutils.build_ext(कम से कम साइथॉन संस्करण 0.14 के साथ) हमेशा .cस्रोत .pyxफ़ाइल के समान निर्देशिका में एक फ़ाइल बनाने के लिए लगता है ।

यहाँ एक कट-डाउन संस्करण है setup.pyजो मुझे आशा है कि आवश्यक दिखाता है:

from distutils.core import setup
from distutils.extension import Extension

try:
    from Cython.Distutils import build_ext
except ImportError:
    use_cython = False
else:
    use_cython = True

cmdclass = {}
ext_modules = []

if use_cython:
    ext_modules += [
        Extension("mypackage.mycythonmodule", ["cython/mycythonmodule.pyx"]),
    ]
    cmdclass.update({'build_ext': build_ext})
else:
    ext_modules += [
        Extension("mypackage.mycythonmodule", ["cython/mycythonmodule.c"]),
    ]

setup(
    name='mypackage',
    ...
    cmdclass=cmdclass,
    ext_modules=ext_modules,
    ...
)

मैंने यह भी MANIFEST.inसुनिश्चित करने के लिए संपादन किया कि mycythonmodule.cस्रोत वितरण में शामिल है (एक स्रोत वितरण जो इसके साथ बनाया गया है python setup.py sdist):

...
recursive-include cython *
...

मैं mycythonmodule.cसंस्करण नियंत्रण 'ट्रंक' (या मर्क्यूरियल के लिए 'डिफ़ॉल्ट) के लिए प्रतिबद्ध नहीं हूं । जब मैं एक रिलीज करता हूं, तो मुझे यह सुनिश्चित करने के लिए याद रखना होगा python setup.py build_extकि mycythonmodule.cस्रोत कोड वितरण के लिए वर्तमान और अप-टू-डेट है। मैं एक रिलीज शाखा भी बनाता हूं, और शाखा में सी फाइल करता हूं। इस तरह मेरे पास सी फ़ाइल का एक ऐतिहासिक रिकॉर्ड है जो उस रिलीज के साथ वितरित किया गया था।


धन्यवाद, यह वही है जो मुझे एक Pyrex परियोजना के लिए आवश्यक था जिसे मैं खोल रहा हूं! MANIFEST.in ने मुझे एक सेकंड के लिए फंसाया, लेकिन मुझे बस एक लाइन की जरूरत थी। मैं सी फाइल इन सोर्स कंट्रोल ऑफ इंटरेस्ट से शामिल कर रहा हूं, लेकिन मैं आपकी बात देखता हूं कि यह अनावश्यक है।
चुमलीग

मैंने अपने उत्तर को यह समझाने के लिए संपादित किया है कि C फ़ाइल ट्रंक / डिफ़ॉल्ट में कैसे नहीं है, लेकिन एक रिलीज़ शाखा में जोड़ा जाता है।
क्रेग मैकक्यून

1
@CraigMcQueen महान जवाब के लिए धन्यवाद, इसने मुझे बहुत मदद की! हालांकि, मैं सोच रहा हूं कि क्या उपलब्ध होने पर साइथन का उपयोग करना वांछित व्यवहार है? यह मुझे लगता है कि डिफ़ॉल्ट रूप से पूर्व-निर्मित सी फ़ाइलों का उपयोग करना बेहतर होगा, जब तक कि उपयोगकर्ता स्पष्ट रूप से साइथन का उपयोग नहीं करना चाहता है, जिस स्थिति में वह पर्यावरण चर या कुछ और सेट कर सकता है। यह इंस्टॉलेशन को अधिक स्थिर / मजबूत बना देगा, क्योंकि उपयोगकर्ता को साइथन के किस संस्करण के आधार पर अलग-अलग परिणाम मिल सकते हैं - उसे यह भी पता नहीं चल सकता है कि उसने इसे स्थापित किया है और यह पैकेज के निर्माण को प्रभावित कर रहा है।
मार्टिंसोस

20

क्रेग मैकक्वीन के जवाब में जोड़ना: sdistसाइथॉन को स्वचालित रूप से स्रोत वितरण बनाने से पहले अपने स्रोत फ़ाइलों को संकलित करने के लिए कमांड को ओवरराइड करने के तरीके के लिए नीचे देखें ।

इस तरह आपके रन से पुराने Cस्रोतों को गलती से वितरित करने का कोई जोखिम नहीं है। यह उस मामले में भी मदद करता है, जहां आप वितरण प्रक्रिया पर सीमित नियंत्रण रखते हैं जैसे कि स्वचालित रूप से निरंतर एकीकरण से वितरण बनाते समय आदि।

from distutils.command.sdist import sdist as _sdist

...

class sdist(_sdist):
    def run(self):
        # Make sure the compiled Cython files in the distribution are up-to-date
        from Cython.Build import cythonize
        cythonize(['cython/mycythonmodule.pyx'])
        _sdist.run(self)
cmdclass['sdist'] = sdist

19

http://docs.cython.org/en/latest/src/userguide/source_files_and_compilation.html#distributing-cython-modules

यह दृढ़ता से अनुशंसा की जाती है कि आप उत्पन्न .c फ़ाइलों के साथ-साथ अपने Cython स्रोतों को भी वितरित करें, ताकि उपयोगकर्ता आपके मॉड्यूल को बिना Cython उपलब्ध कराए स्थापित कर सकें।

यह भी अनुशंसा की जाती है कि आपके द्वारा वितरित किए गए संस्करण में डिफ़ॉल्ट रूप से साइथन संकलन सक्षम न हो। यहां तक ​​कि अगर उपयोगकर्ता के पास साइथन स्थापित है, तो वह संभवतः इसका उपयोग केवल आपके मॉड्यूल को स्थापित करने के लिए नहीं करना चाहता। इसके अलावा, वह संस्करण जो आपके द्वारा उपयोग किया गया वही नहीं हो सकता है, और आपके स्रोतों को सही ढंग से संकलित नहीं कर सकता है।

इसका सीधा सा मतलब यह है कि आपके द्वारा शिप की गई सेटअप-थ्रू फ़ाइल केवल जेनरेट .c फाइलों पर एक सामान्य डिस्टुटिल फ़ाइल होगी, जिसके लिए हमारे पास मूल उदाहरण के लिए होगा:

from distutils.core import setup
from distutils.extension import Extension
 
setup(
    ext_modules = [Extension("example", ["example.c"])]
)

7

सबसे आसान दोनों को शामिल करना है लेकिन सिर्फ सी-फाइल का उपयोग करना है? .Pyx फ़ाइल शामिल करना अच्छा है, लेकिन आपके पास एक बार .c फ़ाइल होने के बाद भी इसकी आवश्यकता नहीं है। जो लोग .pyx को recompile करना चाहते हैं, वे Pyrex स्थापित कर सकते हैं और इसे मैन्युअल रूप से कर सकते हैं।

अन्यथा आपको डिस्टल्यूट के लिए एक कस्टम बिल्ड_ कस्टम कमांड की आवश्यकता है जो पहले सी फाइल बनाता है। साइथॉन में पहले से ही एक शामिल है।http://docs.cython.org/src/userguide/source_files_and_compilation.html

वह दस्तावेज जो नहीं करता है वह कहता है कि यह सशर्त कैसे बनाया जाए, लेकिन

try:
     from Cython.distutils import build_ext
except ImportError:
     from distutils.command import build_ext

इसे संभालना चाहिए।


1
आपके उत्तर के लिए धन्यवाद। यह उचित है, हालांकि मैं पसंद करता हूं अगर साइथॉन स्थापित होने पर setup.pyसीधे .pyxफ़ाइल से निर्माण कर सकता है। मेरे जवाब ने उस पर भी अमल किया है।
क्रेग मैकक्यून

खैर, यह मेरे जवाब का पूरा बिंदु है। यह सिर्फ एक पूरा सेटअप थ्रेड नहीं था।
लेनार्ट रेगेब्रुक

4

सहित (साइथन) उत्पन्न .c फ़ाइलें बहुत अजीब हैं। खासतौर पर तब जब हम इसे गिट में शामिल करते हैं। मैं setuptools_cython का उपयोग करना पसंद करूंगा । जब साइथॉन उपलब्ध नहीं है, तो यह एक अंडे का निर्माण करेगा जिसमें अंतर्निहित साइथॉन वातावरण है, और फिर अंडे का उपयोग करके अपने कोड का निर्माण करें।

एक संभावित उदाहरण: https://github.com/douban/greenify/blob/master/setup.py


अद्यतन (2017/01/05):

चूंकि setuptools 18.0, उपयोग करने की कोई आवश्यकता नहीं है setuptools_cythonयहाँ एक उदाहरण है बिना खरोंच के साइथन परियोजना का निर्माणsetuptools_cython


जब आप इसे setup_requires में निर्दिष्ट करते हैं तो भी यह साइथन के मुद्दे को ठीक नहीं करता है?
कामिल सिंडी

भी 'setuptools>=18.0'विधि बनाने के बजाय setup_requires में डालना संभव नहीं है is_installed?
कामिल सिंडी

1
@capitalistpug सबसे पहले आप सुनिश्चित करने की आवश्यकता setuptools>=18.0तो आप केवल लगाने की जरूरत है, स्थापित किया गया है 'Cython >= 0.18'में setup_requires, और Cython प्रगति स्थापना के दौरान स्थापित किया जाएगा। लेकिन अगर आप setuptools <18.0 का उपयोग कर रहे हैं, यहां तक ​​कि आप setup_requires में विशिष्ट साइथन भी हैं, तो यह इंस्टॉल नहीं किया जाएगा, इस मामले में, आपको उपयोग पर विचार करना चाहिए setuptools_cython
मैकेल्विन

धन्यवाद @McKelvin, यह एक महान समाधान के रूप में लगता है! क्या कोई कारण है कि हमें दूसरे फाइलों का उपयोग करना चाहिए, इसके लिए स्रोत फ़ाइलों को पहले से ही साइथोनाइजिंग के साथ? मैंने आपके दृष्टिकोण की कोशिश की और इसे स्थापित करते समय कुछ धीमा प्रतीत होता है (स्थापित करने में एक मिनट लगता है लेकिन एक सेकंड में निर्माण होता है)।
मार्टिंसोस

1
@ मर्टिंसोस pip install wheel। फिर इसका कारण होना चाहिए। कृपया पहले पहिया स्थापित करें और फिर से प्रयास करें।
मैकल्विन

2

यह एक सेटअप स्क्रिप्ट है जिसे मैंने लिखा था जो बिल्ड के अंदर नेस्टेड निर्देशिकाओं को शामिल करना आसान बनाता है। एक पैकेज के भीतर फ़ोल्डर से इसे चलाने की जरूरत है।

इस तरह की संरचना:

__init__.py
setup.py
test.py
subdir/
      __init__.py
      anothertest.py

setup.py

from setuptools import setup, Extension
from Cython.Distutils import build_ext
# from os import path
ext_names = (
    'test',
    'subdir.anothertest',       
) 

cmdclass = {'build_ext': build_ext}
# for modules in main dir      
ext_modules = [
    Extension(
        ext,
        [ext + ".py"],            
    ) 
    for ext in ext_names if ext.find('.') < 0] 
# for modules in subdir ONLY ONE LEVEL DOWN!! 
# modify it if you need more !!!
ext_modules += [
    Extension(
        ext,
        ["/".join(ext.split('.')) + ".py"],     
    )
    for ext in ext_names if ext.find('.') > 0]

setup(
    name='name',
    ext_modules=ext_modules,
    cmdclass=cmdclass,
    packages=["base", "base.subdir"],
)
#  Build --------------------------
#  python setup.py build_ext --inplace

मुबारक संकलन;)


2

मैं जिस साधारण हैक के साथ आया था:

from distutils.core import setup

try:
    from Cython.Build import cythonize
except ImportError:
    from pip import pip

    pip.main(['install', 'cython'])

    from Cython.Build import cythonize


setup(…)

यदि यह आयात नहीं किया जा सकता है तो बस साइथन को स्थापित करें। एक को शायद इस कोड को साझा नहीं करना चाहिए, लेकिन मेरी अपनी निर्भरता के लिए यह काफी अच्छा है।


2

अन्य सभी जवाब या तो भरोसा करते हैं

  • distutils
  • से आयात करना Cython.Build, जो साइथन की आवश्यकता के बीच चिकन और अंडे की समस्या पैदा करता है setup_requiresऔर इसे आयात करता है।

एक आधुनिक समाधान इसके बजाय सेटपूलों का उपयोग करना है, इस उत्तर को देखें (साइथन एक्सटेंशन के स्वचालित हैंडलिंग के लिए सेटप्टूल 18.0 की आवश्यकता है, अर्थात, यह पहले से ही कई वर्षों के लिए उपलब्ध है)। setup.pyआवश्यकताओं से निपटने के लिए एक आधुनिक मानक , एक प्रवेश बिंदु और एक साइथॉन मॉड्यूल इस तरह दिख सकता है:

from setuptools import setup, Extension

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

setup(
    name='MyPackage',
    install_requires=requirements,
    setup_requires=[
        'setuptools>=18.0',  # automatically handles Cython extensions
        'cython>=0.28.4',
    ],
    entry_points={
        'console_scripts': [
            'mymain = mypackage.main:main',
        ],
    },
    ext_modules=[
        Extension(
            'mypackage.my_cython_module',
            sources=['mypackage/my_cython_module.pyx'],
        ),
    ],
)

Cython.Buildसेटअप समय से आयात करना मेरे लिए ImportError का कारण बनता है। Pyx संकलित करने के लिए सेटप्टूल होने के बाद इसे करने का सबसे अच्छा तरीका है।
कार्सन इप

1

सबसे आसान तरीका है कि मैंने फीचर डिस्टिलिट्स के बजाय केवल सेटप्टूल का उपयोग किया है

from setuptools import setup
from setuptools.extension import Extension
try:
    from Cython.Build import cythonize
except ImportError:
    use_cython = False
else:
    use_cython = True

ext_modules = []
if use_cython:
    ext_modules += cythonize('package/cython_module.pyx')
else:
    ext_modules += [Extension('package.cython_module',
                              ['package/cython_modules.c'])]

setup(name='package_name', ext_modules=ext_modules)

वास्तव में, सेटप्टूल के साथ Cython.Build, मेरे जवाब को देखने के लिए स्पष्ट कोशिश / कैच किए गए आयात की कोई आवश्यकता नहीं है ।
bluenote10

0

मुझे लगता है कि मैंने एक कस्टम build_extकमांड प्रदान करके इसे करने का एक बहुत अच्छा तरीका पाया । विचार निम्नलिखित है:

  1. मैं फ़ंक्शन के शरीर में ओवरराइड करने finalize_options()और करने से सुन्न हेडर जोड़ता हूं import numpy, जो setup()इसे स्थापित करने से पहले सुन्न नहीं होने की समस्या से बचा जाता है।

  2. यदि सिस्टम पर साइथॉन उपलब्ध है, तो यह कमांड की check_extensions_list()विधि में हुक करता है और सभी आउट-ऑफ-डेट साइथॉन मॉड्यूलों को साइथोनाइज करता है, उन्हें सी एक्सटेंशन के साथ प्रतिस्थापित करता है जो बाद में build_extension() विधि द्वारा संभाला जा सकता है । हम अपने मॉड्यूल में कार्यक्षमता का उत्तरार्द्ध भी प्रदान करते हैं: इसका मतलब है कि अगर साइथन उपलब्ध नहीं है, लेकिन हमारे पास एक सी एक्सटेंशन मौजूद है, यह अभी भी काम करता है, जो आपको स्रोत वितरण करने की अनुमति देता है।

यहाँ कोड है:

import re, sys, os.path
from distutils import dep_util, log
from setuptools.command.build_ext import build_ext

try:
    import Cython.Build
    HAVE_CYTHON = True
except ImportError:
    HAVE_CYTHON = False

class BuildExtWithNumpy(build_ext):
    def check_cython(self, ext):
        c_sources = []
        for fname in ext.sources:
            cname, matches = re.subn(r"(?i)\.pyx$", ".c", fname, 1)
            c_sources.append(cname)
            if matches and dep_util.newer(fname, cname):
                if HAVE_CYTHON:
                    return ext
                raise RuntimeError("Cython and C module unavailable")
        ext.sources = c_sources
        return ext

    def check_extensions_list(self, extensions):
        extensions = [self.check_cython(ext) for ext in extensions]
        return build_ext.check_extensions_list(self, extensions)

    def finalize_options(self):
        import numpy as np
        build_ext.finalize_options(self)
        self.include_dirs.append(np.get_include())

यह एक को setup()आयात के बारे में चिंता किए बिना सिर्फ तर्क लिखने की अनुमति देता है और क्या किसी के पास साइथन उपलब्ध है:

setup(
    # ...
    ext_modules=[Extension("_my_fast_thing", ["src/_my_fast_thing.pyx"])],
    setup_requires=['numpy'],
    cmdclass={'build_ext': BuildExtWithNumpy}
    )
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.