setuptools: ตำแหน่งโฟลเดอร์ข้อมูลแพ็คเกจ


98

ฉันใช้ setuptools เพื่อแจกจ่ายแพ็คเกจ python ของฉัน ตอนนี้ฉันต้องการแจกจ่ายดาต้าไฟล์เพิ่มเติม

จากสิ่งที่ฉันรวบรวมจากเอกสาร setuptools ฉันจำเป็นต้องมีไฟล์ข้อมูลของฉันอยู่ในไดเร็กทอรีแพ็คเกจ อย่างไรก็ตามฉันอยากให้ datafiles ของฉันอยู่ในไดเร็กทอรีย่อยในไดเร็กทอรีราก

สิ่งที่ฉันต้องการหลีกเลี่ยง:

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

สิ่งที่ฉันต้องการแทน:

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

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


9
ฉันถามคำถามที่คล้ายกันเกี่ยวกับการใช้ 'data_files' เพื่อแจกจ่ายทรัพยากร (เอกสารรูปภาพ ฯลฯ ): stackoverflow.com/questions/5192386/… ... และ (สอง) คำตอบที่บอกว่าให้ใช้ 'package_data' แทน ตอนนี้ฉันกำลังใช้ข้อมูลแพ็กเกจ แต่นั่นหมายความว่าฉันต้องใส่ข้อมูลและเอกสารของฉันไว้ในแพ็คเกจของฉันเช่นผสมในซอร์สโค้ดของฉัน ฉันไม่ชอบสิ่งนี้ เมื่อดึงซอร์สของฉันฉันไม่พบเพียงนิยามคลาสที่ฉันกำลังค้นหา แต่ยังมีการกล่าวถึงอีกมากมายที่อยู่ในไฟล์ RST, HTML และไฟล์ระดับกลางของฉัน :-(
Jonathan Hartley

2
ฉันรู้ว่าการตอบกลับนี้ช้ามาก @JonathanHartley แต่คุณสามารถสร้าง "แพ็กเกจ" ไดเร็กทอรีใดก็ได้โดยการเพิ่ม__init__.pyไฟล์แม้ว่าไฟล์นั้นจะว่างเปล่าก็ตาม ดังนั้นคุณสามารถแยกไดเร็กทอรีข้อมูลด้วย__init__.pyไฟล์เปล่าเพื่อให้ดูเหมือนแพ็กเกจ สิ่งนี้ควรป้องกันไม่ให้ grep จากภายในซอร์สทรีของคุณหยิบมันขึ้นมา แต่ python และเครื่องมือสร้างมันจะยังรับรู้ว่าเป็นแพ็คเกจ
dhj

@dhj ความคิดที่น่าสนใจขอบคุณ
Jonathan Hartley

4
@dhj ปัญหาเดียวของวิธีการนั้นคือ python คิดว่าคุณได้ติดตั้งแพ็คเกจที่เรียกว่า 'data' หากแพ็กเกจอื่นที่คุณติดตั้งพยายามจัดแพ็กเกจข้อมูลในลักษณะเดียวกันคุณจะต้องติดตั้งแพ็กเกจ 'ข้อมูล' ที่ขัดแย้งกันสองแพ็กเกจ
นิ้วเท้า

คำตอบ:


112

ตัวเลือกที่ 1: ติดตั้งเป็นข้อมูลแพ็คเกจ

ข้อได้เปรียบหลักของการวางไฟล์ข้อมูลไว้ในรูทของแพ็คเกจ Python คือช่วยให้คุณไม่ต้องกังวลว่าไฟล์จะอยู่ที่ใดในระบบของผู้ใช้ซึ่งอาจเป็น Windows, Mac, Linux, แพลตฟอร์มมือถือบางตัวหรือภายใน Egg คุณสามารถค้นหาไดเร็กทอรีที่dataสัมพันธ์กับรูทแพ็กเกจ Python ของคุณได้เสมอไม่ว่าจะติดตั้งที่ใดหรืออย่างไร

ตัวอย่างเช่นถ้าฉันมีเค้าโครงโครงการดังนี้:

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

คุณสามารถเพิ่มฟังก์ชันเพื่อ__init__.pyค้นหาเส้นทางแบบสัมบูรณ์ไปยังไฟล์ข้อมูล:

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')

ผลลัพธ์:

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

หลังจากติดตั้งโปรเจ็กต์เป็น Egg แล้วเส้นทางdataจะเปลี่ยนไป แต่โค้ดไม่จำเป็นต้องเปลี่ยน:

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

ตัวเลือกที่ 2: ติดตั้งในตำแหน่งที่แน่นอน

อีกทางเลือกหนึ่งคือวางข้อมูลของคุณไว้นอกแพ็คเกจ Python จากนั้น:

  1. มีตำแหน่งของการdataส่งผ่านไฟล์การกำหนดค่าอาร์กิวเมนต์บรรทัดคำสั่งหรือ
  2. ฝังตำแหน่งลงในโค้ด Python ของคุณ

สิ่งนี้เป็นที่ต้องการน้อยกว่ามากหากคุณวางแผนที่จะแจกจ่ายโครงการของคุณ หากคุณต้องการทำสิ่งนี้จริงๆคุณสามารถติดตั้งได้dataทุกที่ที่คุณต้องการบนระบบเป้าหมายโดยระบุปลายทางสำหรับแต่ละกลุ่มไฟล์โดยส่งผ่านรายการสิ่งที่เพิ่มเข้ามา:

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

อัปเดต : ตัวอย่างของฟังก์ชันเชลล์สำหรับไฟล์ grep Python แบบวนซ้ำ:

atlas% function grep_py { find . -name '*.py' -exec grep -Hn $* {} \; }
atlas% grep_py ": \["
./setup.py:9:    package_data={'foo': ['data/resource1/foo.txt']}

7
ขอบคุณมากที่ช่วยตกลงกับสถานการณ์นี้ ดังนั้นฉันยินดีที่จะเรียกใช้โดยใช้ package_data ตามที่คุณ (และทุกคน) แนะนำ อย่างไรก็ตาม: มีเพียงฉันคนเดียวที่พบว่าการวางข้อมูลและเอกสารไว้ในไดเร็กทอรีต้นทางของแพ็กเกจจะยุ่งเหยิงไม่สะดวก? (เช่นการ grepping แหล่งที่มาของฉันจะส่งคืน Hit ที่ไม่ต้องการจำนวนมากจากเอกสารของฉันฉันสามารถเพิ่มพารามิเตอร์ '--exclude-dir' ใน grep ทุกครั้งที่ฉันเคยใช้ซึ่งจะแตกต่างจากโปรเจ็กต์หนึ่งไปอีกโปรเจ็กต์หนึ่ง แต่ดูเหมือนว่าน่าเบื่อ) คือ เป็นไปได้ที่จะรวม subdir 'src' ไว้ในหีบห่อของฉันโดยไม่ทำลายการนำเข้า ฯลฯ
Jonathan Hartley

ฉันมักจะใส่เฉพาะไฟล์ข้อมูลที่แพคเกจต้องการภายใต้แพคเกจ dir data_filesฉันจะติดตั้งเอกสารเป็น นอกจากนี้คุณสามารถสร้างนามแฝงเชลล์สำหรับ grep เพื่อละเว้นไฟล์ที่ไม่ใช่ Python ได้เช่นgrep_py.
samplebias

เฮ้ samplebias ขอบคุณสำหรับการอัปเดต ไม่ใช่แค่ grep แต่เป็นทุกอย่างตั้งแต่โปรแกรมแก้ไขข้อความค้นหาในไฟล์ไปจนถึง ctags ไปจนถึง awk ฉันจะลอง reorging โครงการของฉันเพื่อใส่เอกสารใน data_files ตามที่คุณแนะนำดูวิธีการทำงาน กลับเร็ว ๆ นี้ ... :-)
Jonathan Hartley

... ดูเหมือนจะได้ผลดี ขอบคุณที่ทำให้ฉันมาถูกทาง คะแนนชื่อเสียง +50 อร่อยไหม?
Jonathan Hartley

ขอบคุณ! ยินดีที่ได้รับทราบดีใจที่ได้ผลและคุณกำลังก้าวหน้า!
samplebias

14

ฉันคิดว่าฉันพบการประนีประนอมที่ดีซึ่งจะช่วยให้คุณสามารถรักษาโครงสร้างต่อไปนี้:

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

คุณควรติดตั้งข้อมูลเป็น package_data เพื่อหลีกเลี่ยงปัญหาที่อธิบายไว้ในคำตอบ samplebias แต่เพื่อรักษาโครงสร้างไฟล์คุณควรเพิ่มลงใน setup.py ของคุณ:

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

ด้วยวิธีนี้เราจะสร้างโครงสร้างที่เหมาะสม "ทันเวลา" และจัดระเบียบแผนผังแหล่งที่มาของเรา

ในการเข้าถึงไฟล์ข้อมูลดังกล่าวภายในรหัสของคุณคุณเพียงแค่ใช้:

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

ฉันยังไม่ชอบที่จะต้องระบุ 'mypackage' ในโค้ดเนื่องจากข้อมูลอาจไม่มีอะไรจำเป็นสำหรับโมดูลนี้ แต่ฉันคิดว่ามันเป็นการประนีประนอมที่ดี


-4

ผมคิดว่าคุณโดยทั่วไปสามารถให้อะไรเป็นอาร์กิวเมนต์ * * * * * * * * data_files การติดตั้ง ()


อืม ... เห็นว่ามันอยู่ในเอกสาร distutils มองไม่เห็นในเอกสาร setuptools เลย อย่างไรก็ตามในที่สุดฉันจะสามารถเข้าถึงได้อย่างไร
phant0m

ฉันคิดว่า data_files ควรใช้สำหรับข้อมูลที่แชร์ระหว่างแพ็กเกจต่างๆเท่านั้น ตัวอย่างเช่นหากคุณ pip ติดตั้งจาก PyPI ไฟล์ที่อยู่ใน data_files จะถูกติดตั้งไปยังไดเร็กทอรีโดยตรงภายใต้การติดตั้ง Python หลักของคุณ (เช่นไม่อยู่ใน Python27 / Lib / site-Packages / mypackage แต่ใช้คู่ขนานกับ 'Python27 / Lib')
Jonathan Hartley
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.