Termasuk file non-Python dengan setup.py

200

Bagaimana cara saya membuatnya setup.py menyertakan file yang bukan bagian dari kode? (Secara khusus, ini adalah file lisensi, tetapi bisa juga hal lain.)

Saya ingin dapat mengontrol lokasi file. Di folder sumber asli, file tersebut di root paket. (Yaitu pada tingkat yang sama dengan yang paling atas __init__.py.) Saya ingin tetap persis di sana ketika paket diinstal, terlepas dari sistem operasi. Bagaimana aku melakukan itu?

Ram Rachum
sumber
bagaimana Anda melakukannya saat ini? pertanyaan Anda sebelumnya menunjukkan bahwa Anda terbiasa dengan cara menambahkan file lisensi, jadi apa kode Anda yang "tidak berfungsi"?
SilentGhost
2
data_files = [('', ['lgpl2.1_license.txt',]),]menempatkannya di folder Python26.
Ram Rachum
Setelah mendapat umpan balik negatif, saya membaca pertanyaan Anda lagi dan menyadari apa yang saya lewatkan. Saya telah memperbarui jawaban saya untuk memberikan solusi non-peretasan untuk pertanyaan Anda yang tidak memerlukan modul tambahan (seperti setuptools atau distribusikan).
Evan Plaice
Terima kasih Evan. Namun, saya sangat baik-baik saja dengan menggunakan setuptools, karena sangat lazim.
Ram Rachum

Jawaban:

224

Mungkin cara terbaik untuk melakukan ini adalah dengan menggunakan setuptools package_dataarahan. Ini berarti menggunakan setuptools(atau distribute) alih-alih distutils, tetapi ini adalah "peningkatan" yang sangat mulus.

Berikut ini contoh lengkap (tetapi belum teruji):

from setuptools import setup, find_packages

setup(
    name='your_project_name',
    version='0.1',
    description='A description.',
    packages=find_packages(exclude=['ez_setup', 'tests', 'tests.*']),
    package_data={'': ['license.txt']},
    include_package_data=True,
    install_requires=[],
)

Perhatikan baris spesifik yang sangat penting di sini:

package_data={'': ['license.txt']},
include_package_data=True,

package_dataadalah dictnama paket (kosong = semua paket) ke daftar pola (dapat menyertakan gumpalan). Misalnya, jika Anda hanya ingin menentukan file di dalam paket Anda, Anda dapat melakukannya juga:

package_data={'yourpackage': ['*.txt', 'path/to/resources/*.txt']}

Solusinya di sini jelas bukan untuk mengganti nama non- pyfile Anda dengan a.py ekstensi.

Lihat presentasi Ian Bicking untuk info lebih lanjut.

UPDATE: Pendekatan [Lebih Baik] Lain

Pendekatan lain yang berfungsi dengan baik jika Anda hanya ingin mengontrol konten distribusi sumber ( sdist) dan memiliki file di luar paket (misalnya direktori tingkat atas) adalah menambahkan MANIFEST.infile. Lihat dokumentasi Python untuk format file ini.

Sejak menulis respons ini, saya telah menemukan bahwa menggunakan MANIFEST.inbiasanya merupakan pendekatan yang kurang membuat frustrasi untuk memastikan distribusi sumber Anda ( tar.gz) memiliki file yang Anda butuhkan.

Misalnya, jika Anda ingin memasukkan requirements.txtdari tingkat atas, secara rekursif sertakan direktori "data" tingkat atas:

include requirements.txt
recursive-include data *

Namun demikian, agar file-file ini dapat disalin pada waktu instalasi ke folder paket di dalam paket-situs, Anda harus menyediakan include_package_data=Trueke setup()fungsi tersebut. Lihat Menambahkan File Non-Kode untuk informasi lebih lanjut.

Hans L
sumber
5
package_data juga tersedia untuk skrip pengaturan distutils murni sejak Python 2.3.
Éric Araujo
15
Jawaban ini terlihat masuk akal, tetapi tidak berhasil untuk saya. Karena package_data terkenal tidak dapat diandalkan (membutuhkan koordinasi MANIFEST.in dan setup.py untuk menambahkan file ke sdist dan menginstalnya, sebagai langkah terpisah) dan pembuat jawaban ini mencatat "tidak diuji", adakah yang bisa Atau konfirmasi apakah itu bekerja untuk mereka? File LICENSE saya termasuk dalam sdist, tetapi tidak diinstal ketika saya menjalankan "python setup.py install" atau "pip install Package"
Jonathan Hartley
11
Presentasi Ian Bicking hanya menunjukkan cara menginstal data paket untuk file yang ada dalam suatu paket. File LICENSE saya berada di tingkat atas proyek saya, yaitu tidak dalam paket apa pun. Masih bisakah saya menggunakan package_data? Menggunakan data_files adalah non-starter, karena menempatkan file di lokasi yang luas sistem. tidak terkait dengan proyek saya, dan untuk membuatnya lebih buruk, lokasi berubah tergantung pada apakah saya menjalankan "setup.py install" atau "pip install", dari sdist yang sama.
Jonathan Hartley
8
Saya menduga alasan mengapa itu tidak berhasil bagi saya adalah bahwa file tersebut tidak terletak di dalam paket apa pun - ini adalah file LICENSE di tingkat atas repositori, dan karenanya tidak dapat diinstal menggunakan 'package_data'
Jonathan Hartley
7
Jawaban ini tidak cocok untuk saya. File-file tambahan tidak dimasukkan ke dalam tarball ...
lpapp
44

Untuk mencapai apa yang Anda gambarkan akan diperlukan dua langkah ...

  • File perlu ditambahkan ke tarball sumber
  • setup.py perlu dimodifikasi untuk menginstal file data ke jalur sumber

Langkah 1: Untuk menambahkan file ke tarball sumber, sertakan di MANIFEST

Buat template MANIFEST di folder yang berisi setup.py

MANIFEST pada dasarnya adalah file teks dengan daftar semua file yang akan dimasukkan dalam tarball sumber.

Seperti inilah tampilan MANIFEST untuk proyek saya:

  • CHANGELOG.txt
  • INSTALL.txt
  • LICENSE.txt
  • pypreprocessor.py
  • README.txt
  • setup.py
  • test.py
  • TODO.txt

Catatan: Sementara sdist tidak menambahkan beberapa file secara otomatis , saya lebih memilih untuk secara eksplisit menentukan mereka untuk memastikan bukannya memprediksi apa yang dilakukan dan tidak.

Langkah 2: Untuk menginstal file data ke folder sumber, ubah setup.py

Karena Anda ingin menambahkan file data (LICENSE.txt) ke folder instal sumber, Anda perlu memodifikasi jalur instal data agar sesuai dengan path instal sumber. Ini diperlukan karena, secara default, file data dipasang ke lokasi yang berbeda dari file sumber.

Untuk mengubah dir instalasi data agar sesuai dengan dir ...

Tarik info instal dir dari distutils dengan:

from distutils.command.install import INSTALL_SCHEMES

Ubah direktori pemasangan data agar sesuai dengan direktori pemasangan sumber:

for scheme in INSTALL_SCHEMES.values():
    scheme['data'] = scheme['purelib']

Dan, tambahkan file data dan lokasi ke setup ():

data_files=[('', ['LICENSE.txt'])]

Catatan: Langkah-langkah di atas harus mencapai apa yang Anda jelaskan dengan cara standar tanpa memerlukan pustaka ekstensi apa pun.

Evan Plaice
sumber
10
MANIFEST hanya mengontrol file yang termasuk dalam tarball sumber (diproduksi oleh sdist). File yang terdaftar di sana tidak akan diinstal.
David Cournapeau
@ David Saya tidak menyadari seberapa jauh saya dalam pendekatan pertama saya. Saya telah memperbarui jawabannya agar benar untuk menyelesaikan apa yang ditanyakan pertanyaan tanpa memerlukan perpustakaan pihak ketiga tambahan.
Evan Plaice
3
@ Éric Ada alasan khusus mengapa? dan, apakah Anda memiliki alternatif pemasang yang layak yang tidak memerlukan paket pihak ketiga (seperti setup_tools) untuk berfungsi. Saya memilih distutils daripada setuptools karena sudah termasuk instalasi vanilla python dan saya sedang membangun modul untuk PYPI. Seharusnya ada cara yang lebih baik untuk melakukan ini sekarang menggunakan distutils2 tapi saya belum menyentuh python cukup lama jadi saya tidak akan tahu caranya. Karena Anda tampaknya berpengetahuan tentang distutils2 saya pikir itu akan menguntungkan kita semua untuk memiliki alternatif distutils2 yang tepat.
Evan Plaice
6
Seperti yang telah disebutkan di utas lain package_datatidak berfungsi jika file tidak ada dalam paket.
Gringo Suave
2
@ ÉricAraujo: Bukan ide yang buruk untuk menggunakan solusi ini karena tidak ada cara lain. Ini adalah desain distutils yang buruk - itu benar. Tapi itu adalah API publik de-facto yang tidak akan pernah berubah, karena akan merusak banyak hal. Mari berharap distutils2 akan memberikan cara yang disarankan lebih baik.
anatoly techtonik
15

buat MANIFEST.indi root proyek dengan recursive-includeke direktori yang diperlukan atau includedengan nama file.

include LICENSE
include README.rst
recursive-include package/static *
recursive-include package/templates *

dokumentasi dapat ditemukan di sini

Semuanya
sumber
7

Saya ingin mengirim komentar ke salah satu pertanyaan tetapi saya tidak memiliki reputasi yang cukup untuk melakukan itu>.>

Inilah yang bekerja untuk saya (muncul setelah merujuk dokumen):

package_data={
    'mypkg': ['../*.txt']
},

include_package_data: False

Baris terakhir, anehnya, juga penting bagi saya (Anda juga dapat menghilangkan argumen kata kunci ini - kerjanya sama).

Apa yang dilakukan adalah menyalin semua file teks di direktori tingkat atas atau root Anda (satu tingkat lebih tinggi dari paket mypkg yang ingin Anda bagikan).

Semoga ini membantu!

rv.kvetch
sumber
Saya mencari cara untuk tidak harus membuat MANIFEST.in, ini berhasil untuk saya. Baris terakhir juga penting bagi saya. Baris saya adalahinclude_package_data=False, package_data={ "": ["../CHANGELOG.md"] },
Mendhak
7

Langkah 1: buat MANIFEST.infile di folder yang sama dengan setup.py

Langkah 2: sertakan jalur relatif ke file yang ingin Anda tambahkanMANIFEST.in

include README.rst
include docs/*.txt
include funniest/data.json

Langkah 3: atur include_package_data=Truedalam setup()fungsi untuk menyalin file-file ini ke paket situs

Referensi ada di sini.

debuglife
sumber
6

Ini tahun 2019, dan inilah yang berfungsi - meskipun ada saran di sana-sini, apa yang saya temukan di internet setengah didokumentasikan menggunakan setuptools_scm, disahkan sebagai opsi untuksetuptools.setup . Ini akan mencakup semua file data yang diversi pada VCS Anda, baik itu git atau yang lain, ke paket roda, dan akan membuat "pip install" dari repositori git untuk membawa file-file itu.

Jadi, saya baru saja menambahkan dua baris ini ke panggilan pengaturan pada "setup.py". Tidak diperlukan pemasangan atau impor tambahan:

    setup_requires=['setuptools_scm'],
    include_package_data=True,

Tidak perlu mendaftar secara manual package_data, atau dalam file MANIFEST.in - jika sudah diversi, itu sudah termasuk dalam paket. Dokumen pada "setuptools_scm" menekankan pada pembuatan nomor versi dari posisi komit, dan mengabaikan bagian yang sangat penting dari menambahkan file data. (Saya tidak peduli jika file roda perantara saya dinamai "* 0.2.2.dev45 + g3495a1f" atau akan menggunakan nomor versi hardcoded "0.3.0dev0" yang saya ketikkan - tetapi meninggalkan file penting untuk program agar kerja di belakang agak penting)

jsbueno
sumber
5

Di setup.py di bawah pengaturan (:

setup(
   name = 'foo library'
   ...
  package_data={
   'foolibrary.folderA': ['*'],     # All files from folder A
   'foolibrary.folderB': ['*.txt']  #All text files from folder B
   },
Gagah Adam Hughes
sumber
1
Ini sebenarnya tidak melakukan apa-apa untuk mencapai tujuan OP. Apa pun yang Anda tulis tidak package_dataakan mempengaruhi apa yang setup.py installdilakukan, kecuali jika Anda memodifikasi perintah instal itu sendiri. Kecuali jika file-file tersebut berada di bawah direktori paket, yang biasanya merupakan sesuatu yang ingin Anda hindari.
wvxvw
3

Ini jawaban sederhana yang berhasil buat saya.

Pertama, sesuai komentar Python Dev di atas, setuptools tidak diperlukan:

package_data is also available to pure distutils setup scripts 
since 2.3.  Éric Araujo

Itu bagus karena meletakkan persyaratan setuptools pada paket Anda berarti Anda harus menginstalnya juga. Pendeknya:

from distutils.core import setup

setup(
    # ...snip...
    packages          = ['pkgname'],
    package_data      = {'pkgname': ['license.txt']},
)
Gringo Suave
sumber
1
Ini akan mengeluh direktori pkgametidak ada
Anthony Kong
1

Saya hanya ingin menindaklanjuti sesuatu yang saya temukan bekerja dengan Python 2.7 pada Centos 6. Menambahkan package_data atau data_files seperti yang disebutkan di atas tidak bekerja untuk saya. Saya menambahkan MANIFEST.IN dengan file yang saya inginkan yang meletakkan file non-python ke tarball, tetapi tidak menginstalnya pada mesin target melalui RPM.

Pada akhirnya, saya bisa memasukkan file ke dalam solusi saya menggunakan "opsi" di setup / setuptools. File opsi memungkinkan Anda memodifikasi berbagai bagian file spesifikasi dari setup.py. Sebagai berikut.

from setuptools import setup


setup(
    name='theProjectName',
    version='1',
    packages=['thePackage'],
    url='',
    license='',
    author='me',
    author_email='[email protected]',
    description='',
    options={'bdist_rpm': {'install_script': 'filewithinstallcommands'}},
)

file - MANIFEST.in:

include license.txt

file - filewengan perintah instal:

mkdir -p $RPM_BUILD_ROOT/pathtoinstall/
#this line installs your python files
python setup.py install -O1 --root=$RPM_BUILD_ROOT --record=INSTALLED_FILES
#install license.txt into /pathtoinstall folder
install -m 700 license.txt $RPM_BUILD_ROOT/pathtoinstall/
echo /pathtoinstall/license.txt >> INSTALLED_FILES
Scott Bowers
sumber
-12

Menemukan solusi: Saya mengganti nama saya lgpl2.1_license.txtmenjadi lgpl2.1_license.txt.py, dan menaruh beberapa tanda kutip di sekitar teks. Sekarang saya tidak perlu menggunakan data_filesopsi atau untuk menentukan jalur absolut. Membuatnya modul Python jelek, saya tahu, tapi saya menganggapnya kurang jelek daripada menentukan jalur absolut.

Ram Rachum
sumber
7
Lihat posting saya. Itu tidak harus jelek. Sulit menemukan contoh yang bagus di internet karena dokumentasi yang baik untuk menyiapkan paket sulit ditemukan.
Evan Plaice