Skrip pasca-pemasangan dengan alat pengaturan Python

101

Apakah mungkin untuk menentukan file skrip Python pasca-instal sebagai bagian dari file setuptools setup.py sehingga pengguna dapat menjalankan perintah:

python setup.py install

pada arsip file proyek lokal, atau

pip install <name>

untuk proyek PyPI dan skrip akan dijalankan setelah instalasi standar setuptools selesai? Saya mencari untuk melakukan tugas pasca-pemasangan yang dapat dikodekan dalam satu file skrip Python (misalnya, mengirimkan pesan pasca-pemasangan khusus kepada pengguna, menarik file data tambahan dari repositori sumber jarak jauh yang berbeda).

Saya menemukan jawaban SO ini dari beberapa tahun yang lalu yang membahas topik dan kedengarannya seolah-olah konsensus pada saat itu adalah bahwa Anda perlu membuat subperintah instal. Jika masih demikian, apakah mungkin seseorang memberikan contoh bagaimana melakukan ini sehingga pengguna tidak perlu memasukkan perintah kedua untuk menjalankan skrip?

Chris Simpkins
sumber
4
Saya berharap untuk mengotomatiskan skrip yang dijalankan daripada meminta pengguna untuk memasukkan perintah kedua. Ada pemikiran?
Chris Simpkins
1
Ini mungkin yang Anda cari: stackoverflow.com/questions/17806485/…
limp_chimp
1
Terima kasih! Saya akan memeriksanya
Chris Simpkins
1
Jika Anda memang membutuhkan ini, posting blog yang saya temukan oleh google cepat ini sepertinya akan berguna. (Juga lihat Memperluas dan Menggunakan Kembali Alat Setup di dokumen.)
abarnert
1
@Simon Nah, Anda melihat komentar dari 4 tahun yang lalu tentang sesuatu yang mungkin bukan yang diinginkan oleh seseorang dengan masalah ini, jadi Anda tidak dapat mengharapkannya untuk dipantau dan terus diperbarui. Jika ini adalah jawaban, akan sepadan dengan upaya untuk menemukan sumber daya baru untuk menggantikannya, tetapi ternyata tidak. Jika Anda membutuhkan informasi yang sudah ketinggalan zaman, Anda selalu dapat menggunakan Mesin Wayback, atau Anda dapat mencari bagian yang setara di dokumen saat ini.
abarnert

Jawaban:

93

Catatan: Solusi di bawah ini hanya berfungsi ketika menginstal zip atau tarball distribusi sumber, atau menginstal dalam mode yang dapat diedit dari pohon sumber. Ini tidak akan berfungsi saat memasang dari roda biner ( .whl)


Solusi ini lebih transparan:

Anda akan membuat beberapa tambahan setup.pydan tidak perlu file tambahan.

Anda juga perlu mempertimbangkan dua pasca-instalasi yang berbeda; satu untuk mode pengembangan / dapat diedit dan yang lainnya untuk mode penginstalan.

Tambahkan dua kelas ini yang menyertakan skrip pasca-pemasangan Anda ke 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

dan masukkan cmdclassargumen untuk setup()berfungsi di setup.py:

setup(
    ...

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

    ...
)

Anda bahkan dapat memanggil perintah shell selama instalasi, seperti dalam contoh ini yang melakukan persiapan pra-instalasi:

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 tidak ada titik masuk pra-instal yang tersedia di alat setup. Bacalah pembahasan ini jika Anda bertanya-tanya mengapa tidak ada.

mertyildiran
sumber
Terlihat lebih bersih daripada yang lain, tetapi bukankah ini mengeksekusi kode kustom sebeluminstall perintah sebenarnya ?
raphinesse
7
Terserah Anda: jika Anda memanggil runinduknya terlebih dahulu, maka perintah Anda adalah pasca-pemasangan, jika tidak maka pra-instal. Saya telah memperbarui jawaban untuk mencerminkan ini.
kynan
1
menggunakan solusi ini tampaknya install_requiresdependensi diabaikan
ealfonso
7
Ini tidak berhasil untuk saya pip3. Skrip instal berjalan saat menerbitkan paket, tetapi tidak saat menginstalnya.
Eric Wiener
1
@JuanAntonioOrozco Saya telah memperbarui tautan yang rusak menggunakan Mesin Wayback. Saya tidak tahu mengapa itu rusak saat ini. Mungkin ada yang salah dengan bugs.python.org sekarang.
mertyildiran
15

Catatan: Solusi di bawah ini hanya berfungsi ketika menginstal zip atau tarball distribusi sumber, atau menginstal dalam mode yang dapat diedit dari pohon sumber. Ini tidak akan berfungsi saat memasang dari roda biner ( .whl)


Ini adalah satu-satunya strategi yang berhasil untuk saya ketika skrip pasca-instal mengharuskan dependensi paket telah diinstal:

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},
Apalala
sumber
Mengapa Anda mendaftarkan atexitpenangan daripada hanya memanggil fungsi pasca instal setelah langkah instalasi?
kynan
1
@kynan Karena setuptoolskurang terdokumentasi. Orang lain telah mengubah jawaban mereka pada Q&A ini dengan solusi yang benar.
Apalala
3
Jawaban lain tidak berfungsi untuk saya: skrip post install tidak dijalankan, atau dependensi tidak ditangani lagi. Sejauh ini, saya akan tetap berpegang pada atexitdan tidak mendefinisikan ulang install.run()(inilah alasan mengapa dependensi tidak ditangani lagi). Selain itu, untuk mengetahui direktori instal, saya telah menempatkan _post_install()metode new_install, apa yang memungkinkan saya mengakses self.install_purelibdan self.install_platlib(tidak tahu mana yang akan digunakan, tetapi self.install_libsalah, anehnya).
zezollo
2
Saya juga mengalami masalah dengan dependensi dan atexit berfungsi untuk saya
ealfonso
7
Tidak ada metode di sini yang tampaknya berhasil dengan roda. Roda tidak menjalankan setup.py jadi, pesan hanya ditampilkan saat membangun, bukan saat menginstal paket.
JCGB
7

Catatan: Solusi di bawah ini hanya berfungsi ketika menginstal zip atau tarball distribusi sumber, atau menginstal dalam mode yang dapat diedit dari pohon sumber. Ini tidak akan berfungsi saat memasang dari roda biner ( .whl)


Sebuah solusi bisa untuk menyertakan post_setup.pydalam setup.pydirektori 's. post_setup.pyakan berisi fungsi yang melakukan pasca-pemasangan dan setup.pyhanya akan mengimpor dan meluncurkannya pada waktu yang tepat.

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

Masuk post_setup.py:

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

if __name__ == '__main__':
    main()

Dengan gagasan umum untuk meluncurkan setup.pydari direktorinya, Anda akan dapat mengimpor post_setup.pyjika tidak, itu akan meluncurkan fungsi kosong.

Di post_setup.py, if __name__ == '__main__':pernyataan memungkinkan Anda untuk meluncurkan secara manual pasca-pemasangan dari baris perintah.

Zulu
sumber
4
Dalam kasus saya, menimpa run()menyebabkan dependensi paket tidak diinstal.
Apalala
1
@Apalala itu karena yang salah cmdclassdiganti, saya sudah perbaiki ini.
kynan
1
Ah, akhirnya, kami menemukan jawaban yang tepat. Mengapa jawaban yang salah mendapatkan banyak suara di StackOverflow? Memang, Anda harus menjalankan Anda post_install() setelah itu install_data.run(self)jika tidak Anda akan kehilangan beberapa hal. data_filesSetidaknya suka . Terima kasih kynan.
personal_cloud
1
Tidak bekerja untuk saya. Saya kira, untuk alasan apa pun, perintah install_datatersebut tidak dijalankan dalam kasus saya. Jadi, bukankah atexitkeuntungan memastikan skrip pasca instalasi akan dijalankan pada akhirnya, dalam situasi apa pun?
zezollo
3

Menggabungkan jawaban dari @Apalala, @Zulu dan @mertyildiran; ini berhasil untuk saya di lingkungan 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},
...

Ini juga memberi Anda akses ke jalur instalasi paket di install_path, untuk melakukan beberapa pekerjaan shell.

Ezbob
sumber
2

Menurut saya, cara termudah untuk melakukan pasca-pemasangan, dan memenuhi persyaratannya, adalah dengan menghias panggilan ke 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=['...
    )
)

Ini akan berjalan setup()saat mendeklarasikan setup. Setelah selesai dengan instalasi persyaratan, itu akan menjalankan _post_install()fungsi, yang akan menjalankan fungsi bagian dalam _post_actions().

Mbm
sumber
1
Apakah kamu sudah mencobanya? Saya mencoba dengan Python 3.4 dan menginstal berfungsi seperti biasa tetapi post_actions tidak dijalankan ...
dojuba
1

Jika menggunakan atexit, tidak perlu membuat cmdclass baru. Anda cukup membuat register atexit tepat sebelum panggilan setup (). Itu melakukan hal yang sama.

Selain itu, jika Anda memerlukan dependensi untuk diinstal terlebih dahulu, ini tidak berfungsi dengan pip install karena atexit handler Anda akan dipanggil sebelum pip memindahkan paket ke tempatnya.

myjay610
sumber
Seperti beberapa saran yang diposting di sini, yang satu ini tidak memperhitungkan apakah Anda menjalankan mode "instal" atau tidak. Itulah alasan mengapa kelas "perintah" khusus digunakan.
BuvinJ
1

Saya tidak dapat menyelesaikan masalah dengan rekomendasi yang disajikan, jadi inilah yang membantu saya.

Anda dapat memanggil fungsi, bahwa Anda ingin menjalankan setelah instalasi setelah setup()di setup.py, seperti itu:

from setuptools import setup

def _post_install():
    <your code>

setup(...)

_post_install()
sdrenn00.dll
sumber