setuptools: lokasi folder data paket

98

Saya menggunakan setuptools untuk mendistribusikan paket python saya. Sekarang saya perlu mendistribusikan file data tambahan.

Dari apa yang saya kumpulkan dari dokumentasi setuptools, saya perlu memiliki file data saya di dalam direktori paket. Namun, saya lebih suka file data saya di dalam subdirektori di direktori root.

Apa yang ingin saya hindari:

/ #root
|- src/
|  |- mypackage/
|  |  |- data/
|  |  |  |- resource1
|  |  |  |- [...]
|  |  |- __init__.py
|  |  |- [...]
|- setup.py

Yang ingin saya miliki:

/ #root
|- data/
|  |- resource1
|  |- [...]
|- src/
|  |- mypackage/
|  |  |- __init__.py
|  |  |- [...]
|- setup.py

Saya hanya merasa tidak nyaman memiliki begitu banyak subdirektori, jika tidak penting. Saya gagal menemukan alasan, mengapa saya / memiliki / meletakkan file di dalam direktori paket. Juga merepotkan untuk bekerja dengan begitu banyak subdirektori bersarang IMHO. Atau adakah alasan bagus yang membenarkan pembatasan ini?

phant0m
sumber
9
Saya mengajukan pertanyaan serupa tentang menggunakan 'data_files' untuk mendistribusikan sumber daya (dokumen, gambar, dll): stackoverflow.com/questions/5192386/… ... dan (dua) tanggapan keduanya mengatakan untuk menggunakan 'package_data' sebagai gantinya. Sekarang saya menggunakan data paket, tetapi itu berarti saya harus meletakkan data dan dokumen saya di dalam paket saya, yaitu dicampur di antara kode sumber saya. Saya tidak suka ini. Saat grep sumber saya, saya tidak hanya menemukan definisi kelas yang saya cari, tetapi juga lusinan sebutan yang mereka dapatkan dalam file RST, HTML, dan perantara saya. :-(
Jonathan Hartley
2
Saya tahu respons ini sangat terlambat, @JonathanHartley, tetapi Anda dapat menjadikan direktori apa pun sebagai "paket" dengan menambahkan __init__.pyfile, meskipun file itu kosong. Jadi Anda bisa memisahkan direktori data dengan __init__.pyfile kosong agar terlihat seperti sebuah paket. Itu seharusnya mencegah grep dari dalam pohon sumber Anda mengambilnya tetapi itu masih akan dikenali sebagai paket oleh python dan alat pembuatannya.
dhj
@dhj Ide yang menarik, terima kasih.
Jonathan Hartley
4
@dhj satu-satunya masalah dengan pendekatan itu adalah python mengira Anda telah menginstal paket yang disebut 'data'. Jika paket lain yang Anda instal mencoba mengemas data dengan cara yang sama, Anda akan menginstal dua paket 'data' yang bertentangan.
jari kaki

Jawaban:

112

Opsi 1: Instal sebagai paket data

Keuntungan utama menempatkan file data di dalam root paket Python Anda adalah memungkinkan Anda menghindari kekhawatiran tentang di mana file akan tinggal di sistem pengguna, yang mungkin Windows, Mac, Linux, beberapa platform seluler, atau di dalam Egg. Anda selalu dapat menemukan direktori yang databerhubungan dengan root paket Python Anda, tidak peduli di mana atau bagaimana itu diinstal.

Misalnya, jika saya memiliki tata letak proyek seperti ini:

project/
    foo/
        __init__.py
        data/
            resource1/
                foo.txt

Anda dapat menambahkan fungsi __init__.pyuntuk menemukan jalur absolut ke file data:

import os

_ROOT = os.path.abspath(os.path.dirname(__file__))
def get_data(path):
    return os.path.join(_ROOT, 'data', path)

print get_data('resource1/foo.txt')

Keluaran:

/Users/pat/project/foo/data/resource1/foo.txt

Setelah proyek diinstal sebagai Egg, jalur ke dataakan berubah, tetapi kodenya tidak perlu diubah:

/Users/pat/virtenv/foo/lib/python2.6/site-packages/foo-0.0.0-py2.6.egg/foo/data/resource1/foo.txt

Opsi 2: Instal ke lokasi tetap

Alternatifnya adalah menempatkan data Anda di luar paket Python dan kemudian:

  1. Memiliki lokasi yang dataditeruskan melalui file konfigurasi, argumen baris perintah atau
  2. Sematkan lokasi ke dalam kode Python Anda.

Ini jauh lebih tidak diinginkan jika Anda berencana untuk mendistribusikan proyek Anda. Jika Anda benar - benar ingin melakukan ini, Anda dapat menginstal di datamana pun Anda suka pada sistem target dengan menentukan tujuan setiap grup file dengan memasukkan daftar tupel:

from setuptools import setup
setup(
    ...
    data_files=[
        ('/var/data1', ['data/foo.txt']),
        ('/var/data2', ['data/bar.txt'])
        ]
    )

Diperbarui : Contoh fungsi shell untuk grep file Python secara rekursif:

atlas% function grep_py { find . -name '*.py' -exec grep -Hn $* {} \; }
atlas% grep_py ": \["
./setup.py:9:    package_data={'foo': ['data/resource1/foo.txt']}
samplebias
sumber
7
Terima kasih banyak telah membantu saya mengatasi situasi ini. Jadi saya senang menjalankan dengan menggunakan package_data seperti yang Anda (dan orang lain) sarankan. Namun: Apakah hanya saya yang merasa meletakkan data & dokumen mereka di dalam direktori sumber paket menjadi tidak nyaman? (misalnya grepping source saya mengembalikan lusinan hits yang tidak diinginkan dari dokumentasi saya. Saya dapat menambahkan parameter '--exclude-dir' ke grep setiap kali saya menggunakannya, yang akan berbeda dari satu proyek ke proyek berikutnya, tetapi tampaknya menjijikkan) itu mungkin untuk sesuatu seperti memasukkan 'src' subdir di dalam paket dir tanpa merusak impor, dll
Jonathan Hartley
Saya biasanya hanya meletakkan file data yang dibutuhkan paket di bawah dir paket. Saya akan menginstal dokumen sebagai data_files. Juga, Anda bisa membuat alias shell untuk grep untuk mengabaikan file non-Python, seperti grep_py.
samplebias
Hei samplebias. Terima kasih atas pembaruannya. Ini bukan hanya grep, itu segalanya , mulai dari pencarian dalam file editor teks hingga ctag hingga awk. Saya akan mencoba membangun kembali proyek saya untuk meletakkan dokumen di data_files seperti yang Anda sarankan, lihat bagaimana hasilnya. Segera kembali ... :-)
Jonathan Hartley
... sepertinya berhasil. Terima kasih telah menempatkan saya di jalur yang benar. Apakah poin reputasi +50 enak?
Jonathan Hartley
Terima kasih! Senang mendengarnya, senang berhasil dan Anda membuat kemajuan!
samplebias
14

Saya rasa saya menemukan kompromi yang baik yang akan memungkinkan Anda untuk mempertahankan struktur berikut:

/ #root
|- data/
|  |- resource1
|  |- [...]
|- src/
|  |- mypackage/
|  |  |- __init__.py
|  |  |- [...]
|- setup.py

Anda harus menginstal data sebagai package_data, untuk menghindari masalah yang dijelaskan dalam contoh jawaban, tetapi untuk menjaga struktur file Anda harus menambahkan setup.py Anda:

try:
    os.symlink('../../data', 'src/mypackage/data')
    setup(
        ...
        package_data = {'mypackage': ['data/*']}
        ...
    )
finally:
    os.unlink('src/mypackage/data')

Dengan cara ini kita membuat struktur yang sesuai "tepat pada waktunya", dan menjaga susunan pohon sumber kita.

Untuk mengakses file data tersebut dalam kode Anda, Anda 'cukup' menggunakan:

data = resource_filename(Requirement.parse("main_package"), 'mypackage/data')

Saya masih tidak suka harus menentukan 'mypackage' dalam kode, karena data tidak ada hubungannya dengan modul ini, tapi saya rasa ini adalah kompromi yang baik.

polvoazul.dll
sumber
-4

Saya pikir pada dasarnya Anda dapat memberikan apa pun sebagai argumen * data_files * untuk setup () .

lgautier
sumber
Hmm ... Saya bisa lihat itu ada di dokumentasi distutils, tapi tidak bisa lihat di dokumentasi setuptools. Bagaimanapun, bagaimana saya bisa mengaksesnya pada akhirnya?
phant0m
Saya pikir data_files hanya boleh digunakan untuk data yang dibagi antara beberapa paket. misalnya, jika Anda pip install dari PyPI, maka file yang terdaftar di data_files diinstal ke direktori langsung di bawah dir install Python utama Anda. (mis. tidak dalam Python27 / Lib / site-packages / mypackage, tetapi secara paralel dengan 'Python27 / Lib')
Jonathan Hartley