สคริปต์หลังการติดตั้งด้วย Python setuptools


100

เป็นไปได้หรือไม่ที่จะระบุไฟล์สคริปต์ Python หลังการติดตั้งเป็นส่วนหนึ่งของไฟล์ setuptools setup.py เพื่อให้ผู้ใช้สามารถรันคำสั่ง:

python setup.py install

บนที่เก็บไฟล์โปรเจ็กต์ในเครื่องหรือ

pip install <name>

สำหรับโครงการ PyPI และสคริปต์จะทำงานเมื่อเสร็จสิ้นการติดตั้ง setuptools มาตรฐาน? ฉันต้องการดำเนินการหลังการติดตั้งที่สามารถเข้ารหัสในไฟล์สคริปต์ Python ไฟล์เดียว (เช่นส่งข้อความหลังการติดตั้งที่กำหนดเองไปยังผู้ใช้ดึงไฟล์ข้อมูลเพิ่มเติมจากที่เก็บแหล่งข้อมูลระยะไกลอื่น)

ฉันเจอคำตอบ SO นี้เมื่อหลายปีก่อนที่กล่าวถึงหัวข้อและดูเหมือนว่าฉันทามติในเวลานั้นคือคุณต้องสร้างคำสั่งย่อยการติดตั้ง หากยังคงเป็นเช่นนั้นจะเป็นไปได้หรือไม่ที่ใครบางคนจะให้ตัวอย่างวิธีการดำเนินการนี้โดยที่ผู้ใช้ไม่จำเป็นต้องป้อนคำสั่งที่สองเพื่อรันสคริปต์


4
ฉันหวังว่าจะทำให้สคริปต์ทำงานโดยอัตโนมัติแทนที่จะต้องให้ผู้ใช้ป้อนคำสั่งที่สอง ความคิดใด ๆ ?
Chris Simpkins

1
นี่อาจเป็นสิ่งที่คุณกำลังมองหา: stackoverflow.com/questions/17806485/…
limp_chimp

1
ขอบคุณ! ฉันจะตรวจสอบ
Chris Simpkins

1
หากคุณต้องการสิ่งนี้บล็อกโพสต์ที่ฉันพบโดย Google ฉบับย่อดูเหมือนว่าจะมีประโยชน์ (ดูการขยายและการนำ Setuptools มาใช้ใหม่ในเอกสาร)
ยกเลิก

1
@Simon คุณกำลังดูความคิดเห็นเมื่อ 4 ปีที่แล้วเกี่ยวกับบางสิ่งที่อาจไม่ใช่สิ่งที่คนที่มีปัญหานี้ต้องการดังนั้นคุณจึงไม่สามารถคาดหวังได้ว่าจะได้รับการตรวจสอบและอัปเดตอยู่เสมอ หากนี่เป็นคำตอบก็คุ้มค่าที่จะพยายามหาแหล่งข้อมูลใหม่มาทดแทน แต่ก็ไม่ใช่ หากคุณต้องการข้อมูลที่ล้าสมัยคุณสามารถใช้ Wayback Machine ได้ตลอดเวลาหรือค้นหาส่วนที่เทียบเท่าในเอกสารปัจจุบัน
abarnert

คำตอบ:


93

หมายเหตุ:โซลูชันด้านล่างใช้ได้เฉพาะเมื่อติดตั้ง zip การแจกจ่ายต้นทางหรือ tarball หรือติดตั้งในโหมดที่แก้ไขได้จากแผนผังต้นทาง จะไม่ทำงานเมื่อติดตั้งจากวงล้อไบนารี ( .whl)


โซลูชันนี้มีความโปร่งใสมากขึ้น:

คุณจะเพิ่มข้อมูลเล็กน้อยsetup.pyและไม่จำเป็นต้องมีไฟล์เพิ่มเติม

นอกจากนี้คุณต้องพิจารณาการติดตั้งหลังการติดตั้งที่แตกต่างกันสองแบบ หนึ่งสำหรับโหมดพัฒนา / แก้ไขและอีกโหมดหนึ่งสำหรับโหมดติดตั้ง

เพิ่มสองคลาสที่มีสคริปต์หลังการติดตั้งของคุณไปที่setup.py:

from setuptools import setup
from setuptools.command.develop import develop
from setuptools.command.install import install


class PostDevelopCommand(develop):
    """Post-installation for development mode."""
    def run(self):
        develop.run(self)
        # PUT YOUR POST-INSTALL SCRIPT HERE or CALL A FUNCTION

class PostInstallCommand(install):
    """Post-installation for installation mode."""
    def run(self):
        install.run(self)
        # PUT YOUR POST-INSTALL SCRIPT HERE or CALL A FUNCTION

และแทรกcmdclassอาร์กิวเมนต์ให้กับsetup()ฟังก์ชันในsetup.py:

setup(
    ...

    cmdclass={
        'develop': PostDevelopCommand,
        'install': PostInstallCommand,
    },

    ...
)

คุณสามารถเรียกคำสั่งเชลล์ระหว่างการติดตั้งได้เช่นในตัวอย่างนี้ซึ่งเป็นการเตรียมการก่อนการติดตั้ง:

from setuptools import setup
from setuptools.command.develop import develop
from setuptools.command.install import install
from subprocess import check_call


class PreDevelopCommand(develop):
    """Pre-installation for development mode."""
    def run(self):
        check_call("apt-get install this-package".split())
        develop.run(self)

class PreInstallCommand(install):
    """Pre-installation for installation mode."""
    def run(self):
        check_call("apt-get install this-package".split())
        install.run(self)


setup(
    ...

ปล. ไม่มีจุดเข้าใช้งานก่อนการติดตั้งใน setuptools อ่านการสนทนานี้หากคุณสงสัยว่าเหตุใดจึงไม่มี


ดูสะอาดกว่าคนอื่น ๆ แต่สิ่งนี้ไม่รันโค้ดที่กำหนดเองก่อนinstallคำสั่งจริงหรือ?
raphinesse

7
ขึ้นอยู่กับคุณ: หากคุณโทรหาrunผู้ปกครองก่อนคำสั่งของคุณคือหลังการติดตั้งมิฉะนั้นจะเป็นการติดตั้งล่วงหน้า ฉันได้อัปเดตคำตอบเพื่อแสดงถึงสิ่งนี้แล้ว
kynan

1
การใช้โซลูชันนี้ดูเหมือนว่าการinstall_requiresอ้างอิงจะถูกละเว้น
ealfonso

7
pip3นี้ไม่ได้ทำงานให้ฉันด้วย สคริปต์การติดตั้งทำงานเมื่อเผยแพร่แพ็กเกจ แต่ไม่ใช่เมื่อติดตั้ง
Eric Wiener

1
@JuanAntonioOrozco ฉันได้อัปเดตลิงก์เสียโดยใช้ Wayback Machine ฉันไม่รู้ว่าทำไมมันถึงแตกในตอนนี้ อาจมีบางอย่างผิดปกติกับbugs.python.orgในขณะนี้
mertyildiran

14

หมายเหตุ:โซลูชันด้านล่างใช้ได้เฉพาะเมื่อติดตั้ง zip การแจกจ่ายต้นทางหรือ tarball หรือติดตั้งในโหมดที่แก้ไขได้จากแผนผังต้นทาง จะไม่ทำงานเมื่อติดตั้งจากวงล้อไบนารี ( .whl)


นี่เป็นกลยุทธ์เดียวที่ใช้ได้ผลสำหรับฉันเมื่อสคริปต์หลังการติดตั้งต้องการให้ติดตั้งการอ้างอิงแพ็กเกจแล้ว:

import atexit
from setuptools.command.install import install


def _post_install():
    print('POST INSTALL')


class new_install(install):
    def __init__(self, *args, **kwargs):
        super(new_install, self).__init__(*args, **kwargs)
        atexit.register(_post_install)


setuptools.setup(
    cmdclass={'install': new_install},

เหตุใดคุณจึงลงทะเบียนatexitตัวจัดการแทนที่จะเรียกใช้ฟังก์ชันหลังการติดตั้งหลังจากขั้นตอนการติดตั้ง
kynan

1
@kynan เนื่องจากsetuptoolsมีเอกสารค่อนข้างน้อย คนอื่น ๆ ได้แก้ไขคำตอบของพวกเขาเกี่ยวกับคำถามและคำตอบนี้แล้วพร้อมวิธีแก้ไข
Apalala

3
คำตอบอื่น ๆ ใช้ไม่ได้กับฉัน: ไม่ได้เรียกใช้สคริปต์การติดตั้งโพสต์หรือการอ้างอิงไม่ได้รับการจัดการอีกต่อไป จนถึงตอนนี้ฉันจะยึดติดatexitและไม่ได้กำหนดนิยามใหม่install.run()(นี่คือเหตุผลว่าทำไมการอ้างอิงไม่ได้รับการจัดการอีกต่อไป) นอกจากนี้เพื่อให้ทราบถึงไดเร็กทอรีการติดตั้งฉันได้วาง_post_install()วิธีการnew_installสิ่งที่ช่วยให้ฉันเข้าถึงself.install_purelibและself.install_platlib(ไม่รู้ว่าจะใช้อันself.install_libไหนแต่ผิดแปลก ๆ )
zezollo

2
ฉันยังมีปัญหากับการพึ่งพาและงานที่ต้องออกจากงานสำหรับฉัน
ealfonso

7
ไม่มีวิธีใดที่ดูเหมือนจะใช้ได้กับล้อ ล้อไม่ทำงาน setup.py ดังนั้นข้อความจะปรากฏเฉพาะเมื่อสร้างไม่ใช่เมื่อติดตั้งแพ็คเกจ
JCGB

7

หมายเหตุ:โซลูชันด้านล่างใช้ได้เฉพาะเมื่อติดตั้ง zip การแจกจ่ายต้นทางหรือ tarball หรือติดตั้งในโหมดที่แก้ไขได้จากแผนผังต้นทาง จะไม่ทำงานเมื่อติดตั้งจากวงล้อไบนารี ( .whl)


วิธีการแก้ปัญหาอาจจะรวมถึงpost_setup.pyในsetup.pyไดเรกทอรี 's post_setup.pyจะมีฟังก์ชันที่ทำหลังการติดตั้งและsetup.pyจะนำเข้าและเปิดใช้งานในเวลาที่เหมาะสมเท่านั้น

ในsetup.py:

from distutils.core import setup
from distutils.command.install_data import install_data

try:
    from post_setup import main as post_install
except ImportError:
    post_install = lambda: None

class my_install(install_data):
    def run(self):
        install_data.run(self)
        post_install()

if __name__ == '__main__':
    setup(
        ...
        cmdclass={'install_data': my_install},
        ...
    )

ในpost_setup.py:

def main():
    """Do here your post-install"""
    pass

if __name__ == '__main__':
    main()

ด้วยแนวคิดทั่วไปในการเปิดใช้งานsetup.pyจากไดเร็กทอรีคุณจะสามารถนำเข้าได้post_setup.pyมิฉะนั้นจะเปิดฟังก์ชันว่าง

ในpost_setup.pyการif __name__ == '__main__':คำสั่งช่วยให้คุณสามารถเปิดตัวด้วยตนเองหลังการติดตั้งจากบรรทัดคำสั่ง


4
ในกรณีของฉันการลบล้างrun()ทำให้ไม่สามารถติดตั้งการอ้างอิงแพ็คเกจได้
Apalala

1
@Apalala นั่นเป็นเพราะcmdclassแทนที่ผิดฉันได้แก้ไขสิ่งนี้แล้ว
kynan

1
ในที่สุดเราก็พบคำตอบที่ถูกต้อง ทำไมคำตอบที่ผิดจึงได้รับการโหวตมากมายใน StackOverflow? แน่นอนคุณต้องทำงานของคุณpost_install() หลังจากinstall_data.run(self)มิฉะนั้นคุณจะหายไปบางสิ่ง ถูกใจdata_filesอย่างน้อย. ขอบคุณ kynan.
personal_cloud

1
ไม่ได้ผลสำหรับฉัน ฉันเดาว่าไม่ว่าด้วยเหตุผลใดก็ตามคำสั่งinstall_dataจะไม่ถูกดำเนินการในกรณีของฉัน ดังนั้นจึงไม่มีatexitข้อได้เปรียบในการตรวจสอบให้แน่ใจว่าสคริปต์การติดตั้งโพสต์จะถูกเรียกใช้ในท้ายที่สุดไม่ว่าในสถานการณ์ใด ๆ ?
zezollo

3

รวมคำตอบจาก @Apalala, @Zulu และ @mertyildiran; สิ่งนี้ใช้ได้กับฉันในสภาพแวดล้อม Python 3.5:

import atexit
import os
import sys
from setuptools import setup
from setuptools.command.install import install

class CustomInstall(install):
    def run(self):
        def _post_install():
            def find_module_path():
                for p in sys.path:
                    if os.path.isdir(p) and my_name in os.listdir(p):
                        return os.path.join(p, my_name)
            install_path = find_module_path()

            # Add your post install code here

        atexit.register(_post_install)
        install.run(self)

setup(
    cmdclass={'install': CustomInstall},
...

นอกจากนี้ยังช่วยให้คุณเข้าถึงเส้นทางการติดตั้งของแพ็กเกจในinstall_pathเพื่อทำงานเชลล์


2

ฉันคิดว่าวิธีที่ง่ายที่สุดในการดำเนินการหลังการติดตั้งและรักษาข้อกำหนดคือการตกแต่งการโทรไปที่setup(...):

from setup tools import setup


def _post_install(setup):
    def _post_actions():
        do_things()
    _post_actions()
    return setup

setup = _post_install(
    setup(
        name='NAME',
        install_requires=['...
    )
)

นี้จะทำงานเมื่อประกาศsetup() setupเมื่อทำได้ด้วยการติดตั้งความต้องการก็จะเรียกใช้ฟังก์ชั่นซึ่งจะทำงานฟังก์ชั่นภายใน_post_install()_post_actions()


1
ลองทำดูไหม ฉันกำลังลองใช้ Python 3.4 และการติดตั้งทำงานได้ตามปกติ แต่ post_actions ไม่ได้ดำเนินการ ...
dojuba

1

หากใช้ atexit ไม่จำเป็นต้องสร้าง cmdclass ใหม่ คุณสามารถสร้างการลงทะเบียน atexit ของคุณได้ทันทีก่อนการเรียก setup () มันทำสิ่งเดียวกัน

นอกจากนี้หากคุณต้องการการติดตั้งการอ้างอิงก่อนสิ่งนี้จะใช้ไม่ได้กับการติดตั้ง pip เนื่องจากตัวจัดการ atexit ของคุณจะถูกเรียกก่อนที่ pip จะย้ายแพ็คเกจเข้าที่


เช่นเดียวกับคำแนะนำสองสามข้อที่โพสต์ไว้ที่นี่ข้อนี้ไม่ได้ระบุว่าคุณกำลังทำงานในโหมด "ติดตั้ง" หรือไม่ นั่นคือประเด็นที่ว่าทำไมจึงใช้คลาส "command" ที่กำหนดเอง
BuvinJ

1

ฉันไม่สามารถแก้ปัญหาด้วยคำแนะนำที่นำเสนอได้ดังนั้นนี่คือสิ่งที่ช่วยฉันได้

คุณสามารถเรียกใช้ฟังก์ชันที่คุณต้องการเรียกใช้หลังจากการติดตั้งsetup()ในภายหลังsetup.pyเช่น:

from setuptools import setup

def _post_install():
    <your code>

setup(...)

_post_install()
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.