Łącznie z plikami innymi niż Python z setup.py

200

Jak mogę setup.pydołączyć plik, który nie jest częścią kodu? (W szczególności jest to plik licencji, ale może to być coś innego.)

Chcę mieć możliwość kontrolowania lokalizacji pliku. W oryginalnym folderze źródłowym plik znajduje się w katalogu głównym pakietu. (tj. na tym samym poziomie, co najwyższy __init__.py). Chcę, aby pozostał dokładnie tam, gdzie pakiet jest zainstalowany, niezależnie od systemu operacyjnego. W jaki sposób mogę to zrobić?

Ram Rachum
źródło
jak to robisz w tej chwili? poprzednie pytanie wskazuje, że wiesz, jak dodać plik licencji, więc jaki kod „nie działa”?
SilentGhost
2
data_files = [('', ['lgpl2.1_license.txt',]),]umieszcza go w folderze Python26.
Ram Rachum
Po kilku negatywnych opiniach ponownie przeczytałem twoje pytanie i zrozumiałem, czego mi brakowało. Zaktualizowałem swoją odpowiedź, aby zapewnić niehackerskie rozwiązanie twojego pytania, które nie wymaga żadnych dodatkowych modułów (takich jak setuptools lub distribute).
Evan Plaice,
Dzięki Evan. Jednak jestem całkowicie w porządku z używaniem setuptools, ponieważ jest tak powszechne.
Ram Rachum,

Odpowiedzi:

224

Prawdopodobnie najlepszym sposobem na to jest skorzystanie z setuptools package_datadyrektywy. Oznacza to użycie setuptools(lub distribute) zamiast distutils, ale jest to bardzo płynna „aktualizacja”.

Oto pełny (ale nieprzetestowany) przykład:

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

Zwróć uwagę na konkretne wiersze, które są tutaj krytyczne:

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

package_datajest dictnazwą pakietu (pusta = wszystkie pakiety) do listy wzorców (może zawierać globusy). Na przykład, jeśli chcesz określić tylko pliki w pakiecie, możesz to zrobić również:

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

Rozwiązaniem tutaj jest zdecydowanie nie zmiana nazwy pyplików innych niż pliki z .pyrozszerzeniem.

Zobacz prezentację Iana Bickinga, aby uzyskać więcej informacji.

AKTUALIZACJA: Kolejne podejście [lepsze]

Innym podejściem, które działa dobrze, jeśli chcesz kontrolować zawartość źródłowej dystrybucji ( sdist) i mieć pliki poza pakietem (np. Katalog najwyższego poziomu), jest dodanie MANIFEST.inpliku. Zobacz dokumentacji Pythona do formatu tego pliku.

Od czasu napisania tej odpowiedzi stwierdziłem, że używanie MANIFEST.injest zazwyczaj mniej frustrujące, aby upewnić się, że dystrybucja źródłowa ( tar.gz) zawiera potrzebne pliki.

Na przykład, jeśli chcesz dołączyć requirements.txtz najwyższego poziomu, rekurencyjnie dołącz katalog „danych” najwyższego poziomu:

include requirements.txt
recursive-include data *

Niemniej jednak, aby te pliki mogły zostać skopiowane podczas instalacji do folderu pakietu w pakietach witryn, musisz dostarczyć include_package_data=Truesetup()funkcję. Aby uzyskać więcej informacji, zobacz Dodawanie plików innych niż kodowe .

Hans L.
źródło
5
data_pakietu jest również dostępna dla skryptów instalacyjnych pure distutils od Python 2.3.
Éric Araujo,
15
Ta odpowiedź wygląda rozsądnie, ale dla mnie nie działa. Ponieważ data_pakietu jest notorycznie niewiarygodna (wymaga koordynacji plików MANIFEST.in i setup.py w celu dodania plików do sdist i zainstalowania ich w oddzielnych krokach), a autor tej odpowiedzi zauważa, że ​​„nie jest testowany”, każdy może inaczej potwierdź, czy to działa dla nich? Mój plik LICENCJA jest zawarty w sdist, ale nie jest instalowany, gdy uruchamiam „Python setup.py install” ani „pip install Package”
Jonathan Hartley
11
Prezentacja Iana Bicking'a pokazuje tylko, jak zainstalować dane pakietu dla plików znajdujących się w pakiecie. Mój plik LICENCJA znajduje się na najwyższym poziomie mojego projektu, tzn. Nie ma go w żadnych pakietach. Czy nadal mogę używać danych pakietowych? Używanie plików danych nie uruchamia się, ponieważ umieszcza pliki w lokalizacji systemowej. niezwiązane z moim projektem, a co gorsza, lokalizacja zmienia się w zależności od tego, czy uruchamiam „setup.py install” czy „pip install” z tego samego sdist.
Jonathan Hartley
8
Zgaduję, że powodem, dla którego nie działa dla mnie, jest to, że plik nie znajduje się w żadnym pakiecie - jest to plik LICENCJA na najwyższym poziomie repozytorium, a zatem nie można go zainstalować przy użyciu „data_pakietu”
Jonathan Hartley
7
Ta odpowiedź nie działa dla mnie. Dodatkowe pliki nie są umieszczane w archiwum ...
lpapp,
44

Aby osiągnąć to, co opisujesz, musisz wykonać dwa kroki ...

  • Plik należy dodać do źródłowego archiwum
  • Plik setup.py wymaga modyfikacji, aby zainstalować plik danych w ścieżce źródłowej

Krok 1: Aby dodać plik do źródłowego archiwum, dołącz go do MANIFESTU

Utwórz szablon MANIFEST w folderze, który zawiera setup.py

MANIFEST jest w zasadzie plikiem tekstowym z listą wszystkich plików, które zostaną dołączone do źródłowego archiwum.

Oto jak MANIFEST dla mojego projektu:

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

Uwaga: Podczas sdist robi dodać kilka plików automatycznie , wolę je jednoznacznie określić, aby mieć pewność, zamiast przewidywania co robi i nie robi.

Krok 2: Aby zainstalować plik danych w folderze źródłowym, zmodyfikuj plik setup.py

Ponieważ chcesz dodać plik danych (LICENSE.txt) do źródłowego folderu instalacyjnego, musisz zmodyfikować ścieżkę instalacji danych, aby była zgodna ze źródłową ścieżką instalacji. Jest to konieczne, ponieważ domyślnie pliki danych są instalowane w innej lokalizacji niż pliki źródłowe.

Aby zmodyfikować katalog instalacji danych, aby pasował do źródłowego katalogu instalacji ...

Wyciągnij informacje o katalogu instalacyjnym z destutils za pomocą:

from distutils.command.install import INSTALL_SCHEMES

Zmodyfikuj katalog instalacji danych, aby pasował do źródłowego katalogu instalacji:

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

I dodaj plik danych i lokalizację do setup ():

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

Uwaga: powyższe kroki powinny wykonać dokładnie to, co opisano w standardowy sposób, bez konieczności używania bibliotek rozszerzeń.

Evan Plaice
źródło
10
MANIFEST kontroluje tylko pliki zawarte w źródłowym pliku archiwalnym (wyprodukowanym przez sdist). Pliki tam wymienione nie zostaną zainstalowane.
David Cournapeau,
@ David Nie zdawałem sobie sprawy, jak daleko byłem w moim pierwszym podejściu. Zaktualizowałem odpowiedź, aby była poprawna, aby osiągnąć to, o co pytało pytanie, nie wymagając żadnych dodatkowych bibliotek stron trzecich.
Evan Plaice,
3
@ Éric Jakiś konkretny powód? i czy masz realną alternatywę dla instalatora, która nie wymaga pakietów innych firm (takich jak setup_tools) do działania. Wybrałem distutils zamiast setuptools, ponieważ jest on dołączony do waniliowej instalacji Pythona i budowałem moduły dla PYPI. Powinien być lepszy sposób na zrobienie tego teraz za pomocą distutils2, ale od dłuższego czasu nie dotykałem Pythona, więc nie wiedziałbym, jak to zrobić. Ponieważ wydajesz się mieć wiedzę na temat distutils2, myślę, że reszta z nas skorzystałaby z posiadania odpowiedniej alternatywy distutils2.
Evan Plaice,
6
Jak wspomniano w innych wątkach package_data, nie działa, jeśli pliku nie ma w pakiecie.
Gringo Suave
2
@ ÉricAraujo: Korzystanie z tego rozwiązania nie jest złym pomysłem, ponieważ nie ma innego sposobu. To zły projekt distutils - to prawda. Ale to de facto publiczny interfejs API, który nigdy się nie zmieni, ponieważ zniszczy wiele rzeczy. Miejmy nadzieję, że distutils2 zapewni lepsze zalecane sposoby.
anatoly techtonik
15

utwórz MANIFEST.inw katalogu głównym projektu za pomocą recursive-includedo wymaganego katalogu lub includez nazwą pliku.

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

dokumentacja znajduje się tutaj

Wszystko to
źródło
7

Chciałem opublikować komentarz do jednego z pytań, ale nie mam wystarczającej reputacji, aby to zrobić>.>

Oto, co zadziałało dla mnie (wymyśliłem to po zapoznaniu się z dokumentami):

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

include_package_data: False

Co dziwne, ostatnia linia była dla mnie kluczowa (możesz też pominąć ten argument słowa kluczowego - działa tak samo).

Powoduje to kopiowanie wszystkich plików tekstowych do katalogu najwyższego poziomu lub katalogu głównego (jeden poziom wyżej od pakietu, mypkgktóry chcesz dystrybuować).

Mam nadzieję że to pomoże!

rv.kvetch
źródło
Szukałem sposobu, aby nie musieć tworzyć MANIFEST.in, to zadziałało dla mnie. Ostatnia linia była również dla mnie kluczowa. Moje linie byłyinclude_package_data=False, package_data={ "": ["../CHANGELOG.md"] },
Mendhak
7

Krok 1: Utwórz MANIFEST.inplik w tym samym folderze za pomocą setup.py

Krok 2: dołącz ścieżkę względną do plików, które chcesz dodaćMANIFEST.in

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

Krok 3: ustaw include_package_data=Truew setup()funkcji kopiowanie tych plików do pakietu witryny

Referencje są tutaj.

debuglife
źródło
6

Jest rok 2019 i oto, co działa - pomimo porad tu i tam, to, co znalazłem w Internecie w połowie drogi, jest udokumentowane setuptools_scm, przekazane jako opcje do setuptools.setup. Obejmuje to wszelkie pliki danych, które są wersjonowane na twoim VCS, czy to git, czy jakikolwiek inny, do pakietu wheel, i spowoduje „instalację pip” z repozytorium git, aby zabrać te pliki ze sobą.

Właśnie dodałem te dwie linie do wywołania instalacyjnego na „setup.py”. Nie wymaga dodatkowych instalacji ani importów:

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

Nie trzeba ręcznie wyświetlać danych pakiet_danych ani pliku MANIFEST.in - jeśli jest wersjonowany, jest dołączany do pakietu. Dokumenty na temat „setuptools_scm” kładą nacisk na tworzenie numeru wersji z pozycji zatwierdzenia i pomijają naprawdę ważną część dodawania plików danych. (Nie obchodzi mnie to, czy mój plik koła pośredniego ma nazwę „* 0.2.2.dev45 + g3495a1f” lub użyję zakodowanego numeru wersji „0.3.0dev0”, który wpisałem - ale pozostawiając kluczowe pliki dla programu praca z tyłu jest nieco ważna)

jsbueno
źródło
5

W setup.py w setup (:

setup(
   name = 'foo library'
   ...
  package_data={
   'foolibrary.folderA': ['*'],     # All files from folder A
   'foolibrary.folderB': ['*.txt']  #All text files from folder B
   },
Dashing Adam Hughes
źródło
1
W rzeczywistości nie ma to żadnego wpływu na osiągnięcie celu PO. Cokolwiek napiszesz, nie package_databędzie miało wpływu na to setup.py install, co robi, chyba że zmodyfikujesz samą komendę install. Chyba że te pliki znajdują się w katalogu pakietu, co zwykle jest rzeczą, której należy unikać.
wvxvw
3

Oto prostsza odpowiedź, która zadziałała dla mnie.

Po pierwsze, zgodnie z powyższym komentarzem Pythona Deva setuptools nie jest wymagane:

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

To świetnie, ponieważ nałożenie pakietu na setuptools oznacza, że ​​będziesz musiał go również zainstalować. W skrócie:

from distutils.core import setup

setup(
    # ...snip...
    packages          = ['pkgname'],
    package_data      = {'pkgname': ['license.txt']},
)
Gringo Suave
źródło
1
Będzie narzekać, że katalog pkgamenie istnieje
Anthony Kong
1

Chciałem tylko sprawdzić coś, co znalazłem podczas pracy z Pythonem 2.7 na Centos 6. Dodanie paczek_danych lub plików_danych, jak wspomniano powyżej, nie działało dla mnie. Dodałem plik MANIFEST.IN z plikami, które chciałem, które umieściły pliki inne niż python w archiwum, ale nie zainstalowałem ich na komputerze docelowym za pośrednictwem RPM.

W końcu udało mi się pobrać pliki do mojego rozwiązania za pomocą „opcji” w setup / setuptools. Pliki opcji pozwalają modyfikować różne sekcje pliku specyfikacji z pliku setup.py. Następująco.

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

plik - MANIFEST.in:

include license.txt

plik - plik z poleceniami instalacji:

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
źródło
-12

Wymyśliłem obejście: zmieniłem nazwę lgpl2.1_license.txtna lgpl2.1_license.txt.pyi umieściłem potrójne cytaty wokół tekstu. Teraz nie muszę używać data_filesopcji ani określać żadnych ścieżek bezwzględnych. Zrobienie z niego modułu Pythona jest brzydkie, ale uważam, że jest mniej brzydkie niż określenie absolutnych ścieżek.

Ram Rachum
źródło
7
Zobacz mój post. To nie musi być brzydkie. Trudno jest znaleźć dobry przykład w sieci, ponieważ trudno jest znaleźć dobrą dokumentację do konfiguracji pakietów.
Evan Plaice,