Bao gồm các tệp không phải Python với setup.py


200

Làm cách nào để tạo setup.pymột tệp không phải là một phần của mã? (Cụ thể, đó là một tệp giấy phép, nhưng nó có thể là bất kỳ thứ gì khác.)

Tôi muốn có thể kiểm soát vị trí của tập tin. Trong thư mục nguồn gốc, tệp nằm trong thư mục gốc của gói. (tức là ở cùng cấp độ với mức cao nhất __init__.py.) Tôi muốn nó ở chính xác khi gói được cài đặt, bất kể hệ điều hành. Làm thế nào để làm điều đó?


Làm thế nào để bạn làm điều đó vào lúc này? câu hỏi trước đây của bạn chỉ ra rằng bạn quen thuộc với cách thêm tệp giấy phép, vậy mã của bạn "không hoạt động" là gì?
SilentGhost

2
data_files = [('', ['lgpl2.1_license.txt',]),]đặt nó trong thư mục Python26.
Ram Rachum

Sau một số phản hồi tiêu cực, tôi đọc lại câu hỏi của bạn và nhận ra những gì tôi đang thiếu. Tôi đã cập nhật câu trả lời của mình để cung cấp giải pháp không hack cho câu hỏi của bạn mà không yêu cầu bất kỳ mô-đun bổ sung nào (chẳng hạn như setuptools hoặc phân phối).
Evan Plaice

Cảm ơn Evan. Tuy nhiên, tôi hoàn toàn ổn với việc sử dụng setuptools, vì nó rất phổ biến.
Ram Rachum

Câu trả lời:


224

Có lẽ cách tốt nhất để làm điều này là sử dụng setuptools package_datachỉ thị. Điều này không có nghĩa là sử dụng setuptools(hoặc distribute) thay vì distutils, nhưng đây là một "nâng cấp" rất liền mạch.

Đây là một ví dụ đầy đủ (nhưng chưa được kiểm tra):

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=[],
)

Lưu ý các dòng cụ thể rất quan trọng ở đây:

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

package_datalà một dicttên gói (trống = tất cả các gói) cho một danh sách các mẫu (có thể bao gồm các khối). Ví dụ: nếu bạn muốn chỉ định các tệp trong gói của mình, bạn cũng có thể làm điều đó:

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

Giải pháp ở đây chắc chắn là không đổi tên các pytệp không phải của bạn bằng một .pyphần mở rộng.

Xem phần trình bày của Ian Bicking để biết thêm.

CẬP NHẬT: Cách tiếp cận [Tốt hơn]

Một cách tiếp cận khác hoạt động tốt nếu bạn chỉ muốn kiểm soát nội dung của phân phối nguồn ( sdist) và có các tệp bên ngoài gói (ví dụ: thư mục cấp cao nhất) là thêm MANIFEST.intệp. Xem tài liệu Python để định dạng của tệp này.

Kể từ khi viết phản hồi này, tôi đã thấy rằng việc sử dụng MANIFEST.inthường là một cách tiếp cận ít gây khó chịu hơn để đảm bảo phân phối nguồn của bạn ( tar.gz) có các tệp bạn cần.

Ví dụ: nếu bạn muốn bao gồm requirements.txttừ cấp cao nhất, hãy bao gồm đệ quy thư mục "dữ liệu" cấp cao nhất:

include requirements.txt
recursive-include data *

Tuy nhiên, để các tệp này được sao chép khi cài đặt vào thư mục của gói bên trong các gói trang web, bạn sẽ cần cung cấp include_package_data=Truecho setup()chức năng. Xem Thêm tệp không mã để biết thêm thông tin.


5
pack_data cũng có sẵn cho các tập lệnh thiết lập distutils thuần kể từ Python 2.3.
Éric Araujo

15
Câu trả lời này có vẻ hợp lý, nhưng không hiệu quả với tôi. Vì pack_data nổi tiếng là không đáng tin cậy (yêu cầu phối hợp MANIFEST.in và setup.py để thêm tệp vào sdist và cài đặt chúng, như các bước riêng biệt) và tác giả của câu trả lời này lưu ý rằng "không được thử nghiệm", bất kỳ ai cũng có thể khác xác nhận liệu nó làm việc cho họ? Tệp LICENSE của tôi được bao gồm trong sdist, nhưng không được cài đặt khi tôi chạy "python setup.py install" cũng như "gói cài đặt pip"
Jonathan Hartley

11
Bài thuyết trình của Ian Bicking chỉ cho thấy cách cài đặt dữ liệu gói cho các tệp trong một gói. Tệp LICENSE của tôi ở cấp cao nhất trong dự án của tôi, tức là không có trong bất kỳ gói nào. Tôi vẫn có thể sử dụng gói_data chứ? Sử dụng data_files là không khởi động, vì nó đặt các tệp ở vị trí toàn hệ thống. không liên quan đến dự án của tôi và để làm cho nó tồi tệ hơn, vị trí thay đổi tùy thuộc vào việc tôi chạy "setup.py install" hay "pip install", từ cùng một sdist.
Jonathan Hartley

8
Tôi đoán rằng lý do nó không hoạt động với tôi là vì tệp không nằm trong bất kỳ gói nào - đó là tệp LICENSE ở cấp cao nhất của kho lưu trữ và do đó không thể được cài đặt bằng 'pack_data'
Jonathan Hartley

7
Câu trả lời này không làm việc cho tôi. Các tệp bổ sung sẽ không được đưa vào tarball ...
lpapp

44

Để thực hiện những gì bạn mô tả sẽ mất hai bước ...

  • Các tập tin cần phải được thêm vào tarball nguồn
  • setup.py cần được sửa đổi để cài đặt tệp dữ liệu vào đường dẫn nguồn

Bước 1: Để thêm tệp vào tarball nguồn, hãy đưa nó vào MANIFEST

Tạo một mẫu MANIFEST trong thư mục có chứa setup.py

MANIFEST về cơ bản là một tệp văn bản với danh sách tất cả các tệp sẽ được bao gồm trong tarball nguồn.

Đây là những gì MANIFEST cho dự án của tôi trông như thế nào:

  • CHANGELOG.txt
  • INSTALL.txt
  • LICENSE.txt
  • pypre Processor.py
  • README.txt
  • thiết lập
  • kiểm tra
  • TODO.txt

Lưu ý: Trong khi sdist làm thêm một số tập tin tự động , tôi thích để xác định rõ ràng họ phải chắc chắn thay vì dự đoán những gì nó làm và không.

Bước 2: Để cài đặt tệp dữ liệu vào thư mục nguồn, hãy sửa đổi setup.py

Vì bạn đang tìm cách thêm tệp dữ liệu (LICENSE.txt) vào thư mục cài đặt nguồn, bạn cần sửa đổi đường dẫn cài đặt dữ liệu để khớp với đường dẫn cài đặt nguồn. Điều này là cần thiết bởi vì theo mặc định, các tệp dữ liệu được cài đặt ở một vị trí khác với các tệp nguồn.

Để sửa đổi thư mục cài đặt dữ liệu để phù hợp với thư mục cài đặt nguồn ...

Kéo thông tin dir cài đặt từ distutils với:

from distutils.command.install import INSTALL_SCHEMES

Sửa đổi thư mục cài đặt dữ liệu để phù hợp với thư mục cài đặt nguồn:

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

Và, thêm tệp dữ liệu và vị trí để thiết lập ():

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

Lưu ý: Các bước trên phải thực hiện chính xác những gì bạn mô tả theo cách chuẩn mà không yêu cầu bất kỳ thư viện mở rộng nào.


10
MANIFEST chỉ kiểm soát các tệp có trong tarball nguồn (được tạo bởi sdist). Các tập tin được liệt kê ở đó sẽ không được cài đặt.
David Cournapeau

@David Tôi không nhận ra mình đã đi được bao xa trong lần tiếp cận đầu tiên. Tôi đã cập nhật câu trả lời là chính xác để hoàn thành câu hỏi đang hỏi mà không yêu cầu bất kỳ thư viện bên thứ ba bổ sung nào.
Evan Plaice

3
@ Éric Bất kỳ lý do cụ thể tại sao? và, bạn có một giải pháp thay thế trình cài đặt khả thi không yêu cầu các gói của bên thứ 3 (như setup_tools) để hoạt động không. Tôi đã chọn distutils trên setuptools vì nó đi kèm với một cài đặt pyilla của python và tôi đang xây dựng các mô-đun cho PYPI. Có một cách tốt hơn để làm điều này ngay bây giờ bằng cách sử dụng distutils2 nhưng tôi đã không chạm vào con trăn trong một thời gian dài vì vậy tôi không biết làm thế nào. Vì bạn dường như am hiểu về distutils2, tôi nghĩ rằng nó sẽ có lợi cho phần còn lại của chúng tôi để có một sự thay thế distutils2 thích hợp.
Evan Plaice

6
Như đã được đề cập trong các chủ đề khác package_datakhông hoạt động nếu tập tin không có trong gói.
Gringo Suave

2
@ ÉricAraujo: Sử dụng giải pháp này không phải là ý kiến ​​tồi vì không có cách nào khác. Đó là một thiết kế distutils xấu - đó là sự thật. Nhưng đó là API công khai trên thực tế sẽ không bao giờ thay đổi, vì nó sẽ phá vỡ nhiều thứ. Hãy hy vọng rằng distutils2 sẽ cung cấp những cách được đề xuất tốt hơn.
anatoly techtonik


7

Tôi muốn đăng bình luận cho một trong những câu hỏi nhưng tôi không đủ danh tiếng để làm điều đó>.>

Đây là những gì làm việc cho tôi (đã đưa ra nó sau khi tham khảo các tài liệu):

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

include_package_data: False

Dòng cuối cùng, thật kỳ lạ, cũng rất quan trọng đối với tôi (bạn cũng có thể bỏ qua đối số từ khóa này - nó hoạt động tương tự).

Những gì nó làm là nó sao chép tất cả các tệp văn bản trong thư mục gốc hoặc cấp cao nhất của bạn (tăng một cấp từ gói mypkgbạn muốn phân phối).

Hi vọng điêu nay co ich!


Tôi đang tìm kiếm một cách để không phải tạo ra MANIFEST.in, điều này làm việc cho tôi. Dòng cuối cùng cũng rất quan trọng đối với tôi. Dòng của tôi làinclude_package_data=False, package_data={ "": ["../CHANGELOG.md"] },
Mendhak

7

Bước 1: tạo một MANIFEST.intệp trong cùng thư mục với setup.py

Bước 2: bao gồm đường dẫn tương đối đến các tệp bạn muốn thêm vàoMANIFEST.in

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

Bước 3: thiết lập include_package_data=Truetrong setup()chức năng sao chép những tập tin này vào trang web gói

Tham khảo ở đây.


6

Đó là năm 2019, và đây là những gì đang hoạt động - mặc dù lời khuyên ở đây và ở đó, những gì tôi tìm thấy trên internet được ghi lại bằng tài liệu đang sử dụng setuptools_scm, được chuyển qua dưới dạng tùy chọn setuptools.setup. Điều này sẽ bao gồm bất kỳ tệp dữ liệu nào được phiên bản trên VCS của bạn, có thể là git hoặc bất kỳ tệp nào khác, cho gói bánh xe và sẽ thực hiện "cài đặt pip" từ kho lưu trữ git để mang các tệp đó đi cùng.

Vì vậy, tôi vừa thêm hai dòng này vào lệnh gọi thiết lập trên "setup.py". Không cần cài đặt thêm hoặc nhập yêu cầu:

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

Không cần liệt kê thủ công gói_data hoặc trong tệp MANIFEST.in - nếu được phiên bản, nó được bao gồm trong gói. Các tài liệu về "setuptools_scm" nhấn mạnh vào việc tạo số phiên bản từ vị trí cam kết và bỏ qua phần thực sự quan trọng trong việc thêm các tệp dữ liệu. (Tôi không quan tâm nếu tệp bánh xe trung gian của tôi được đặt tên là "* 0.2.2.dev45 + g3495a1f" hoặc sẽ sử dụng số phiên bản được mã hóa cứng "0.3.0dev0" Tôi đã nhập - nhưng để lại các tệp quan trọng cho chương trình công việc phía sau có phần quan trọng)


5

Trong setup.py theo thiết lập (:

setup(
   name = 'foo library'
   ...
  package_data={
   'foolibrary.folderA': ['*'],     # All files from folder A
   'foolibrary.folderB': ['*.txt']  #All text files from folder B
   },

1
Điều này thực sự không có gì để hoàn thành mục tiêu của OP. Bất cứ điều gì bạn viết vào package_datasẽ không ảnh hưởng đến những gì setup.py install, trừ khi bạn sửa đổi lệnh cài đặt. Trừ khi các tệp đó nằm trong thư mục gói, thường là thứ bạn muốn tránh.
wvxvw

3

Đây là một câu trả lời đơn giản hơn làm việc cho tôi.

Đầu tiên, theo nhận xét của Python Dev ở trên, setuptools không bắt buộc:

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

Điều đó thật tuyệt vì đặt yêu cầu setuptools trên gói của bạn đồng nghĩa với việc bạn cũng sẽ phải cài đặt nó. Nói ngắn gọn:

from distutils.core import setup

setup(
    # ...snip...
    packages          = ['pkgname'],
    package_data      = {'pkgname': ['license.txt']},
)

1
Nó sẽ khiếu nại thư mục pkgamekhông tồn tại
Anthony Kong

1

Tôi chỉ muốn theo dõi một cái gì đó tôi thấy làm việc với Python 2.7 trên Centos 6. Thêm gói_data hoặc data_files như đã đề cập ở trên không hoạt động với tôi. Tôi đã thêm MANIFEST.IN với các tệp tôi muốn đưa các tệp không phải python vào tarball, nhưng không cài đặt chúng trên máy đích thông qua RPM.

Cuối cùng, tôi đã có thể đưa các tệp vào giải pháp của mình bằng cách sử dụng "tùy chọn" trong thiết lập / setuptools. Các tệp tùy chọn cho phép bạn sửa đổi các phần khác nhau của tệp spec từ setup.py. Như sau.

from setuptools import setup


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

tệp - MANIFEST.in:

include license.txt

tập tin - filewithinstallcommands:

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

-12

Tìm ra một cách giải quyết: Tôi đổi tên của tôi lgpl2.1_license.txtđể lgpl2.1_license.txt.py, và đặt một số dấu ngoặc kép ba xung quanh văn bản. Bây giờ tôi không cần sử dụng data_filestùy chọn cũng như chỉ định bất kỳ đường dẫn tuyệt đối nào. Làm cho nó trở thành một mô-đun Python là xấu, tôi biết, nhưng tôi coi nó ít xấu hơn là chỉ định các đường dẫn tuyệt đối.


7
Xem bài viết của tôi. Nó không phải là xấu xí. Thật khó để tìm thấy một ví dụ tốt trên mạng vì tài liệu tốt để thiết lập các gói rất khó tìm.
Evan Plaice
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.