Tập lệnh sau cài đặt với công cụ thiết lập Python


97

Có thể chỉ định tệp kịch bản Python sau cài đặt như một phần của tệp setuptools setup.py để người dùng có thể chạy lệnh:

python setup.py install

trên kho lưu trữ tệp dự án cục bộ, hoặc

pip install <name>

cho một dự án PyPI và tập lệnh sẽ được chạy khi hoàn thành cài đặt công cụ thiết lập tiêu chuẩn? Tôi đang tìm cách thực hiện các tác vụ sau cài đặt có thể được mã hóa trong một tệp kịch bản Python duy nhất (ví dụ: gửi thông báo sau cài đặt tùy chỉnh cho người dùng, kéo các tệp dữ liệu bổ sung từ một kho lưu trữ nguồn từ xa khác).

Tôi đã xem câu trả lời SO này từ vài năm trước đề cập đến chủ đề và có vẻ như sự đồng thuận tại thời điểm đó là bạn cần tạo một lệnh con cài đặt. Nếu trường hợp đó vẫn xảy ra, liệu ai đó có thể cung cấp ví dụ về cách thực hiện việc này để người dùng không cần nhập lệnh thứ hai để chạy tập lệnh không?


4
Tôi hy vọng sẽ tự động hóa quá trình chạy tập lệnh thay vì yêu cầu người dùng nhập lệnh thứ hai. Có suy nghĩ gì không?
Chris Simpkins

1
Đây có thể là những gì bạn đang tìm kiếm: stackoverflow.com/questions/17806485/...
limp_chimp

1
Cảm ơn bạn! Tôi sẽ kiểm tra xem nó ra
Chris Simpkins

1
Nếu bạn cần điều này, bài đăng trên blog mà tôi tìm thấy bằng cách tìm kiếm nhanh trên google có vẻ như nó sẽ hữu ích. (Cũng xem Mở rộng và sử dụng lại các công cụ cài đặt trong tài liệu.)
abarnert

1
@Simon Chà, bạn đang xem một bình luận từ 4 năm trước về điều gì đó có lẽ không phải là điều mà người gặp vấn đề này muốn, vì vậy bạn không thể thực sự mong đợi nó được theo dõi và cập nhật. Nếu đây là một câu trả lời, thì việc tìm kiếm các nguồn tài nguyên mới để thay thế chúng là điều đáng giá, nhưng không phải vậy. Nếu bạn cần thông tin lỗi thời, bạn luôn có thể sử dụng Wayback Machine hoặc bạn có thể tìm kiếm phần tương đương trong tài liệu hiện tại.
abarnert

Câu trả lời:


92

Lưu ý: Giải pháp bên dưới chỉ hoạt động khi cài đặt zip phân phối nguồn hoặc tarball hoặc cài đặt ở chế độ có thể chỉnh sửa từ cây nguồn. Nó sẽ không hoạt động khi cài đặt từ bánh xe nhị phân ( .whl)


Giải pháp này minh bạch hơn:

Bạn sẽ thực hiện một số bổ sung setup.pyvà không cần thêm tệp.

Ngoài ra, bạn cần phải xem xét hai cài đặt sau khác nhau; một cho chế độ phát triển / có thể chỉnh sửa và một cho chế độ cài đặt.

Thêm hai loại cổ phiếu này bao gồm các bạn sau khi cài đặt kịch bản để 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

và chèn cmdclassđối số vào setup()hàm trong setup.py:

setup(
    ...

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

    ...
)

Bạn thậm chí có thể gọi các lệnh shell trong khi cài đặt, như trong ví dụ này chuẩn bị trước khi cài đặt:

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

PS không có bất kỳ điểm nhập cài đặt trước nào có sẵn trên các công cụ thiết lập. Đọc cuộc thảo luận này nếu bạn đang tự hỏi tại sao không có.


Trông gọn gàng hơn những cái khác, nhưng điều này không thực thi mã tùy chỉnh trướcinstall lệnh thực ?
raphinesse

7
Điều đó tùy thuộc vào bạn: nếu bạn gọi runcho cha mẹ trước thì lệnh của bạn là lệnh sau cài đặt, nếu không thì đó là cài đặt trước. Tôi đã cập nhật câu trả lời để phản ánh điều này.
kynan

1
sử dụng giải pháp này dường như install_requiresphụ thuộc sẽ được bỏ qua
ealfonso

7
Điều này không làm việc cho tôi với pip3. Tập lệnh cài đặt đã chạy khi xuất bản gói, nhưng không chạy khi cài đặt nó.
Eric Wiener

1
@JuanAntonioOrozco Tôi đã cập nhật liên kết bị hỏng bằng Wayback Machine. Tôi không biết tại sao nó bị hỏng ngay lúc này. Có thể có vấn đề gì đó với bug.python.org ngay bây giờ.
mertyildiran

14

Lưu ý: Giải pháp bên dưới chỉ hoạt động khi cài đặt zip phân phối nguồn hoặc tarball hoặc cài đặt ở chế độ có thể chỉnh sửa từ cây nguồn. Nó sẽ không hoạt động khi cài đặt từ bánh xe nhị phân ( .whl)


Đây là chiến lược duy nhất phù hợp với tôi khi tập lệnh sau cài đặt yêu cầu rằng các gói phụ thuộc đã được cài đặt:

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},

Tại sao bạn đăng ký một atexittrình xử lý thay vì chỉ gọi hàm post install sau bước cài đặt?
kynan

1
@kynan Bởi vì setuptoolstài liệu khá ít. Những người khác đã sửa đổi câu trả lời của họ trên Hỏi và Đáp này với các giải pháp chính xác.
Apalala

3
Vâng, các câu trả lời khác không phù hợp với tôi: kịch bản cài đặt bài đăng không được thực thi hoặc các phần phụ thuộc không được xử lý nữa. Cho đến nay, tôi sẽ tiếp tục atexitkhông xác định lại install.run()(đây là lý do tại sao các phần phụ thuộc không được xử lý nữa). Ngoài ra, để biết thư mục cài đặt, tôi đã đặt _post_install()như một phương pháp new_install, những gì cho phép tôi truy cập self.install_purelibself.install_platlib(không biết sử dụng cái nào, nhưng lại self.install_libsai, thật kỳ lạ).
zezollo

2
Tôi cũng đã gặp vấn đề với phụ thuộc và các công trình atexit đối với tôi
ealfonso

7
Không có phương pháp nào ở đây dường như hoạt động với bánh xe. Bánh xe không chạy setup.py vì vậy, thông báo chỉ hiển thị khi xây dựng, không hiển thị khi cài đặt gói.
JCGB

7

Lưu ý: Giải pháp bên dưới chỉ hoạt động khi cài đặt zip phân phối nguồn hoặc tarball hoặc cài đặt ở chế độ có thể chỉnh sửa từ cây nguồn. Nó sẽ không hoạt động khi cài đặt từ bánh xe nhị phân ( .whl)


Một giải pháp có thể là đưa post_setup.pyvào setup.pythư mục của. post_setup.pysẽ chứa một chức năng thực hiện cài đặt sau và setup.pysẽ chỉ nhập và khởi chạy nó vào thời điểm thích hợp.

Trong 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},
        ...
    )

Trong post_setup.py:

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

if __name__ == '__main__':
    main()

Với ý tưởng phổ biến là khởi chạy setup.pytừ thư mục của nó, bạn sẽ có thể nhập post_setup.pykhác, nó sẽ khởi chạy một chức năng trống.

Trong post_setup.py, if __name__ == '__main__':câu lệnh cho phép bạn khởi chạy thủ công sau cài đặt từ dòng lệnh.


4
Trong trường hợp của tôi, việc ghi đè run()khiến các gói phụ thuộc không được cài đặt.
Apalala

1
@Apalala được vì sai cmdclassđã được thay thế, tôi đã cố định này.
kynan

1
Ah, cuối cùng, chúng tôi tìm thấy câu trả lời thích hợp. Làm thế nào mà những câu trả lời sai lại nhận được nhiều phiếu bầu như vậy trên StackOverflow? Thật vậy, bạn phải chạy của bạn post_install() sau khi các install_data.run(self)số nội dung nếu không bạn sẽ bị mất. Thích data_filesít nhất. Cảm ơn bạn kynan.
Personal_cloud

1
Không làm việc cho tôi. Tôi đoán, vì bất kỳ lý do gì, lệnh install_datakhông được thực thi trong trường hợp của tôi. Vì vậy, không phải atexitlợi ích của việc đảm bảo cuối cùng kịch bản cài đặt sẽ được thực thi, trong mọi tình huống?
zezollo

3

Kết hợp các câu trả lời từ @Apalala, @Zulu và @mertyildiran; điều này làm việc cho tôi trong môi trường 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},
...

Điều này cũng cho phép bạn truy cập vào đường dẫn cài đặt của gói trong đó install_path, để thực hiện một số công việc trên shell.


2

Tôi nghĩ rằng cách dễ nhất để thực hiện cài đặt sau và giữ các yêu cầu, là trang trí lệnh gọi tới 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=['...
    )
)

Điều này sẽ chạy setup()khi khai báo setup. Sau khi thực hiện quá trình cài đặt yêu cầu, nó sẽ chạy các _post_install()chức năng, mà sẽ chạy các chức năng bên trong _post_actions().


1
Bạn đã thử cái này chưa? Tôi đang cố gắng với Python 3.4 và cài đặt các công trình như bình thường nhưng post_actions không thực hiện ...
dojuba

1

Nếu sử dụng atexit, không cần tạo một cmdclass mới. Bạn chỉ cần tạo đăng ký atexit của mình ngay trước cuộc gọi setup (). Nó làm điều tương tự.

Ngoài ra, nếu bạn cần cài đặt các gói phụ thuộc trước, điều này không hoạt động với cài đặt pip vì trình xử lý atexit của bạn sẽ được gọi trước khi pip chuyển các gói vào vị trí.


Giống như một số đề xuất được đăng ở đây, đề xuất này không giải thích cho việc bạn có đang chạy ở chế độ "cài đặt" hay không. Đó là lý do tại sao các lớp "lệnh" tùy chỉnh được sử dụng.
BuvinJ

1

Tôi không thể giải quyết vấn đề với bất kỳ đề xuất nào được trình bày, vì vậy đây là những gì đã giúp tôi.

Bạn có thể gọi hàm mà bạn muốn chạy sau khi cài đặt ngay sau khi setup()nhập setup.py, như sau:

from setuptools import setup

def _post_install():
    <your code>

setup(...)

_post_install()
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.