Compresi file non Python con setup.py


200

Come faccio a setup.pyincludere un file che non fa parte del codice? (In particolare, è un file di licenza, ma potrebbe essere qualsiasi altra cosa.)

Voglio essere in grado di controllare la posizione del file. Nella cartella di origine originale, il file si trova nella radice del pacchetto. (ovvero allo stesso livello del più alto __init__.py.) Voglio che rimanga esattamente lì quando il pacchetto è installato, indipendentemente dal sistema operativo. Come lo faccio?


come lo fai al momento? la tua domanda precedente indica che hai familiarità con come aggiungere il file di licenza, quindi qual è il tuo codice che "non funziona"?
SilentGhost,

2
data_files = [('', ['lgpl2.1_license.txt',]),]lo mette nella cartella Python26.
Ram Rachum,

Dopo un feedback negativo, ho letto di nuovo la tua domanda e ho capito cosa mi mancava. Ho aggiornato la mia risposta per fornire una soluzione non hacker alla tua domanda che non richiede moduli aggiuntivi (come setuptools o distribut).
Evan Plaice,

Grazie Evan. Tuttavia, sto perfettamente bene con l'utilizzo di setuptools, poiché è così diffuso.
Ram Rachum,

Risposte:


224

Probabilmente il modo migliore per farlo è usare la setuptools package_datadirettiva. Questo significa usare setuptools(o distribute) invece di distutils, ma questo è un "upgrade" molto semplice.

Ecco un esempio completo (ma non testato):

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

Nota le linee specifiche che sono fondamentali qui:

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

package_dataè un nome dictdi pacchetto (vuoto = tutti i pacchetti) in un elenco di modelli (può includere globs). Ad esempio, se si desidera specificare solo i file all'interno del pacchetto, è possibile farlo anche:

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

La soluzione qui è sicuramente non per rinominare i non- pyfile con .pyestensione.

Vedi la presentazione di Ian Bicking per maggiori informazioni.

AGGIORNAMENTO: un altro approccio [migliore]

Un altro approccio che funziona bene se si desidera solo controllare il contenuto della distribuzione di origine ( sdist) e disporre di file all'esterno del pacchetto (ad esempio directory di livello superiore) è quello di aggiungere un MANIFEST.infile. Consulta la documentazione di Python per il formato di questo file.

Da quando ho scritto questa risposta, ho scoperto che l'utilizzo MANIFEST.inè in genere un approccio meno frustrante per assicurarsi che la distribuzione di origine ( tar.gz) abbia i file necessari.

Ad esempio, se si desidera includere il requirements.txtlivello principale, includere ricorsivamente la directory "dati" di livello superiore:

include requirements.txt
recursive-include data *

Tuttavia, per poter copiare questi file al momento dell'installazione nella cartella del pacchetto all'interno dei pacchetti del sito, sarà necessario fornire include_package_data=Truealla setup()funzione. Vedere Aggiunta di file non di codice per ulteriori informazioni.


5
package_data è disponibile anche per gli script di installazione di puro distutils da Python 2.3.
Éric Araujo,

15
Questa risposta sembra ragionevole, ma non funziona per me. Poiché package_data è notoriamente inaffidabile (richiede il coordinamento di MANIFEST.in e setup.py sia per aggiungere file allo sdist che per installarli, come passaggi separati) e l'autore di questa risposta nota che "non è testato", chiunque può altro conferma se funziona per loro? Il mio file LICENSE è incluso nello sdist, ma non installato quando eseguo "python setup.py install" né "pip install Package"
Jonathan Hartley,

11
La presentazione di Ian Bicking mostra solo come installare i dati del pacchetto per i file che si trovano all'interno di un pacchetto. Il mio file LICENSE è al livello più alto del mio progetto, cioè non in nessun pacchetto. Posso ancora usare package_data? L'uso di data_files non è un inizio, perché mette i file in una posizione a livello di sistema. non associato al mio progetto e, a peggiorare le cose, la posizione cambia a seconda che io esegua "setup.py install" o "pip install", dallo stesso sdist.
Jonathan Hartley,

8
Immagino che la ragione per cui non funziona per me è che il file non si trova all'interno di alcun pacchetto - è un file LICENSE al livello superiore del repository e quindi non può essere installato usando 'package_data'
Jonathan Hartley,

7
Questa risposta non funziona per me. I file aggiuntivi non vengono inseriti nel tarball ...
lpapp il

44

Per realizzare ciò che stai descrivendo ci vorranno due passaggi ...

  • Il file deve essere aggiunto al tarball di origine
  • setup.py deve essere modificato per installare il file di dati nel percorso di origine

Passaggio 1: per aggiungere il file al tarball di origine, includerlo nel MANIFEST

Creare un modello MANIFEST nella cartella che contiene setup.py

MANIFEST è fondamentalmente un file di testo con un elenco di tutti i file che verranno inclusi nel tarball di origine.

Ecco come si presenta MANIFEST per il mio progetto:

  • changelog.txt
  • INSTALL.txt
  • LICENSE.txt
  • pypreprocessor.py
  • README.txt
  • setup.py
  • test.py
  • todo.txt

Nota: Mentre sdist fa aggiungere automaticamente alcuni file , preferisco specificare esplicitamente loro di essere sicuri, invece di prevedere ciò che fa e non lo fa.

Passaggio 2: per installare il file di dati nella cartella di origine, modificare setup.py

Poiché stai cercando di aggiungere un file di dati (LICENSE.txt) alla cartella di installazione di origine, devi modificare il percorso di installazione dei dati in modo che corrisponda al percorso di installazione di origine. Ciò è necessario perché, per impostazione predefinita, i file di dati sono installati in una posizione diversa rispetto ai file di origine.

Per modificare la directory di installazione dei dati in modo che corrisponda alla directory di installazione di origine ...

Estrai le informazioni dir installazione da distutils con:

from distutils.command.install import INSTALL_SCHEMES

Modifica la directory di installazione dei dati in modo che corrisponda alla directory di installazione di origine:

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

E aggiungi il file di dati e il percorso a setup ():

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

Nota: i passaggi precedenti dovrebbero realizzare esattamente ciò che è stato descritto in modo standard senza richiedere librerie di estensioni.


10
MANIFEST controlla solo i file inclusi nel tarball sorgente (prodotto da sdist). I file elencati non verranno installati.
David Cournapeau,

@ David Non mi ero reso conto di quanto ero lontano dal mio primo approccio. Ho aggiornato la risposta per essere corretta per realizzare ciò che la domanda stava ponendo senza richiedere ulteriori librerie di terze parti.
Evan Plaice,

3
@ Éric Qualche motivo particolare per cui? e hai un'alternativa valida all'installer che non richiede pacchetti di terze parti (come setup_tools) per funzionare. Ho scelto distutils su setuptools perché è incluso in un'installazione vanilla di Python e stavo costruendo moduli per PYPI. Dovrebbe esserci un modo migliore per farlo ora usando distutils2 ma non tocco Python da un po 'di tempo, quindi non saprei come. Dal momento che sembri essere informato su distutils2, penso che sarebbe utile per il resto di noi avere un'alternativa distutils2 adeguata.
Evan Plaice,

6
Come è stato menzionato in altri thread package_data, non funziona se il file non si trova nel pacchetto.
Gringo Suave,

2
@ ÉricAraujo: non è una cattiva idea utilizzare questa soluzione in quanto non esiste altro modo. È un cattivo disegno di distutils - è vero. Ma è di fatto un'API pubblica che non cambierà mai, perché romperà molte cose. Speriamo che distutils2 fornisca i modi migliori consigliati.
Anatoly Techtonik,


7

Volevo pubblicare un commento a una delle domande ma non ho abbastanza reputazione per farlo>.>

Ecco cosa ha funzionato per me (ne è uscito dopo aver fatto riferimento ai documenti):

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

include_package_data: False

L'ultima riga è stata, stranamente, anche cruciale per me (puoi anche omettere questo argomento della parola chiave - funziona allo stesso modo).

Ciò consente di copiare tutti i file di testo nella directory principale o principale (a un livello superiore dal pacchetto che mypkgsi desidera distribuire).

Spero che questo ti aiuti!


Stavo cercando un modo per non dover creare un MANIFEST.in, questo ha funzionato per me. L'ultima riga è stata cruciale anche per me. Le mie battute eranoinclude_package_data=False, package_data={ "": ["../CHANGELOG.md"] },
Mendhak

7

Passaggio 1: crea un MANIFEST.infile nella stessa cartella con setup.py

Passaggio 2: includere il percorso relativo ai file che si desidera aggiungereMANIFEST.in

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

Passaggio 3: impostare include_package_data=Truela setup()funzione per copiare questi file nel pacchetto del sito

Il riferimento è qui.


6

È il 2019, ed ecco cosa funziona - nonostante i consigli qua e là, quello che ho trovato su Internet a metà documentato sta usando setuptools_scm, passato come opzioni a setuptools.setup. Ciò includerà tutti i file di dati che sono stati sottoposti a versioning sul VCS, sia esso git o qualsiasi altro, nel pacchetto wheel, e farà "installare pip" dal repository git per portare quei file.

Quindi, ho appena aggiunto queste due linee alla chiamata di installazione su "setup.py". Nessuna installazione aggiuntiva o richiesta di importazione:

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

Non è necessario elencare manualmente package_data o in un file MANIFEST.in - se è aggiornato, è incluso nel pacchetto. I documenti su "setuptools_scm" mettono l'accento sulla creazione di un numero di versione dalla posizione di commit e ignorano la parte veramente importante dell'aggiunta dei file di dati. (Non me ne può fregare di meno se il mio file della ruota intermedia si chiama "* 0.2.2.dev45 + g3495a1f" o utilizzerò il numero di versione hardcod "0.3.0dev0" che ho digitato - ma lasciando i file cruciali per il programma lavorare dietro è piuttosto importante)


5

In setup.py sotto setup (:

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

1
Questo in realtà non fa nulla per raggiungere l'obiettivo del PO. Qualunque cosa tu scriva package_datanon avrà influenza su ciò che setup.py installfa, a meno che tu non modifichi il comando install stesso. A meno che quei file non si trovino nella directory del pacchetto, che di solito è qualcosa che vorresti evitare.
wvxvw,

3

Ecco una risposta più semplice che ha funzionato per me.

Innanzitutto, per un commento di Python Dev sopra, setuptools non è richiesto:

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

È fantastico perché mettere un requisito setuptools sul tuo pacchetto significa che dovrai installarlo anche. In breve:

from distutils.core import setup

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

1
Si lamenterà che la directory pkgamenon esiste
Anthony Kong,

1

Volevo solo dare seguito a qualcosa che ho trovato lavorando con Python 2.7 su Centos 6. L'aggiunta di package_data o data_files come menzionato sopra non ha funzionato per me. Ho aggiunto un MANIFEST.IN con i file desiderati che hanno inserito i file non Python nel tarball, ma non li ho installati sul computer di destinazione tramite RPM.

Alla fine, sono stato in grado di ottenere i file nella mia soluzione usando le "opzioni" nei setup / setuptools. I file delle opzioni consentono di modificare varie sezioni del file delle specifiche da setup.py. Come segue.

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

file - MANIFEST.in:

include license.txt

file - file con i comandi di installazione:

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

Capito una soluzione: ho rinominato la mia lgpl2.1_license.txta lgpl2.1_license.txt.py, e mettere un po triple virgolette intorno al testo. Ora non ho bisogno di usare l' data_filesopzione né di specificare percorsi assoluti. Renderlo un modulo Python è brutto, lo so, ma lo considero meno brutto che specificare percorsi assoluti.


7
Vedi il mio post. Non deve essere brutto. È solo difficile trovare un buon esempio in rete perché è difficile trovare una buona documentazione per l'installazione dei pacchetti.
Evan Plaice,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.