Incluyendo archivos que no son de Python con setup.py

200

¿Cómo hago para setup.pyincluir un archivo que no es parte del código? (Específicamente, es un archivo de licencia, pero podría ser cualquier otra cosa).

Quiero poder controlar la ubicación del archivo. En la carpeta de origen original, el archivo está en la raíz del paquete. (es decir, en el mismo nivel que el superior __init__.py). Quiero que permanezca exactamente allí cuando se instale el paquete, independientemente del sistema operativo. ¿Cómo puedo hacer eso?

Ram Rachum
fuente
¿Cómo haces eso en este momento? su pregunta anterior indica que está familiarizado con cómo agregar el archivo de licencia, entonces, ¿cuál es su código que "no funciona"?
SilentGhost
2
data_files = [('', ['lgpl2.1_license.txt',]),]lo pone en la carpeta Python26.
Ram Rachum el
Después de algunos comentarios negativos, leí tu pregunta nuevamente y me di cuenta de lo que me faltaba. He actualizado mi respuesta para proporcionar una solución no pirata a su pregunta que no requiera ningún módulo adicional (como herramientas de configuración o distribución).
Evan Plaice
Gracias Evan Sin embargo, estoy perfectamente de acuerdo con el uso de herramientas de configuración, ya que es muy frecuente.
Ram Rachum

Respuestas:

224

Probablemente la mejor manera de hacerlo es usar la setuptools package_datadirectiva. Esto significa usar setuptools(o distribute) en lugar de distutils, pero esta es una "actualización" muy fluida.

Aquí hay un ejemplo completo (pero no probado):

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

Tenga en cuenta las líneas específicas que son críticas aquí:

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

package_dataes un nombre dictde paquete (vacío = todos los paquetes) a una lista de patrones (puede incluir globos). Por ejemplo, si solo desea especificar archivos dentro de su paquete, también puede hacerlo:

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

La solución aquí es definitivamente no cambiar el nombre de los pyarchivos que no sean con una .pyextensión.

Vea la presentación de Ian Bicking para más información.

ACTUALIZACIÓN: Otro enfoque [mejor]

Otro enfoque que funciona bien si solo desea controlar el contenido de la distribución fuente ( sdist) y tener archivos fuera del paquete (por ejemplo, directorio de nivel superior) es agregar un MANIFEST.inarchivo. Consulte la documentación de Python para ver el formato de este archivo.

Desde que escribí esta respuesta, descubrí que el uso MANIFEST.insuele ser un enfoque menos frustrante para asegurarse de que su distribución de origen ( tar.gz) tenga los archivos que necesita.

Por ejemplo, si desea incluir requirements.txtdesde el nivel superior, incluya recursivamente el directorio de "datos" de nivel superior:

include requirements.txt
recursive-include data *

Sin embargo, para que estos archivos se copien en el momento de la instalación en la carpeta del paquete dentro de los paquetes del sitio, deberá suministrar include_package_data=Truea la setup()función. Consulte Agregar archivos sin código para obtener más información.

Hans L
fuente
55
package_data también está disponible para los scripts de instalación de distutils puros desde Python 2.3.
Éric Araujo
15
Esta respuesta parece sensata, pero no funciona para mí. Dado que package_data es notoriamente poco confiable (requiere la coordinación de MANIFEST.in y setup.py para agregar archivos al sdist e instalarlos, como pasos separados) y el autor de esta respuesta observa que "no se prueba", ¿alguien puede si no confirma si funciona para ellos? Mi archivo de LICENCIA está incluido en el sdist, pero no se instala cuando ejecuto "python setup.py install" ni "pip install Package"
Jonathan Hartley
11
La presentación de Ian Bicking solo muestra cómo instalar los datos del paquete para los archivos que están dentro de un paquete. Mi archivo de LICENCIA está en el nivel superior de mi proyecto, es decir, no está en ningún paquete. ¿Puedo seguir usando package_data? El uso de data_files no es un iniciador, ya que coloca los archivos en una ubicación de todo el sistema. no asociado con mi proyecto, y para empeorarlo, la ubicación cambia dependiendo de si ejecuto "setup.py install" o "pip install", desde el mismo sdist.
Jonathan Hartley
8
Supongo que la razón por la que no funciona para mí es que el archivo no se encuentra dentro de ningún paquete: es un archivo de LICENCIA en el nivel superior del repositorio y, por lo tanto, no se puede instalar usando 'package_data'
Jonathan Hartley
77
Esta respuesta no me funciona. Los archivos adicionales no se ponen en el tarball ...
lpapp
44

Para lograr lo que estás describiendo tomarás dos pasos ...

  • El archivo debe agregarse al archivo tar de origen
  • setup.py necesita ser modificado para instalar el archivo de datos en la ruta de origen

Paso 1: para agregar el archivo al archivo tar de origen, inclúyalo en el MANIFEST

Cree una plantilla MANIFEST en la carpeta que contiene setup.py

El MANIFEST es básicamente un archivo de texto con una lista de todos los archivos que se incluirán en el archivo tar de origen.

Así es como se ve el MANIFEST para mi proyecto:

  • CHANGELOG.txt
  • INSTALL.txt
  • LICENCIA.txt
  • pypreprocessor.py
  • README.txt
  • setup.py
  • prueba.py
  • TODO.txt

Nota: Si bien sdist hace agregar algunos archivos de forma automática , prefiero que especificar explícitamente para estar seguro en lugar de predecir lo que hace y no lo hace.

Paso 2: para instalar el archivo de datos en la carpeta de origen, modifique setup.py

Como está buscando agregar un archivo de datos (LICENSE.txt) a la carpeta de instalación de origen, debe modificar la ruta de instalación de datos para que coincida con la ruta de instalación de origen. Esto es necesario porque, de manera predeterminada, los archivos de datos se instalan en una ubicación diferente a los archivos de origen.

Para modificar el directorio de instalación de datos para que coincida con el directorio de instalación de origen ...

Extraiga la información del directorio de instalación de distutils con:

from distutils.command.install import INSTALL_SCHEMES

Modifique el directorio de instalación de datos para que coincida con el directorio de instalación de origen:

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

Y agregue el archivo de datos y la ubicación a la configuración ():

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

Nota: Los pasos anteriores deben cumplir exactamente lo que describió de manera estándar sin requerir ninguna biblioteca de extensiones.

Evan Plaice
fuente
10
MANIFEST solo controla los archivos incluidos en el tarball fuente (producido por sdist). Los archivos enumerados allí no se instalarán.
David Cournapeau
@David No me di cuenta de cuán lejos estaba en mi primer acercamiento. He actualizado la respuesta para que sea correcta para lograr lo que la pregunta estaba haciendo sin requerir bibliotecas de terceros adicionales.
Evan Plaice
3
@ Éric ¿Alguna razón en particular por qué? y, ¿tiene una alternativa de instalación viable que no requiera paquetes de terceros (como setup_tools) para funcionar. Elegí distutils sobre setuptools porque está incluido con una instalación estándar de python y estaba creando módulos para PYPI. Debería haber una mejor manera de hacerlo ahora usando distutils2, pero no he tocado Python en mucho tiempo, así que no sabría cómo. Como parece que tiene conocimientos sobre distutils2, creo que sería beneficioso para el resto de nosotros tener una alternativa adecuada a distutils2.
Evan Plaice
66
Como se ha mencionado en otros hilos package_data, no funciona si el archivo no está en el paquete.
Gringo Suave
2
@ ÉricAraujo: No es una mala idea usar esta solución ya que no hay otra manera. Es un mal diseño distutils, eso es cierto. Pero es una API pública de facto que nunca cambiará, porque romperá muchas cosas. Esperemos que distutils2 proporcione mejores formas recomendadas.
anatoly techtonik
15

crear MANIFEST.inen la raíz del proyecto con recursive-includeel directorio requerido o includecon el nombre del archivo.

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

la documentación se puede encontrar aquí

Todo es variado
fuente
7

Quería publicar un comentario en una de las preguntas, pero no tengo suficiente reputación para hacerlo>.>

Esto es lo que funcionó para mí (surgió después de referir los documentos):

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

include_package_data: False

La última línea fue, por extraño que parezca, también crucial para mí (también puede omitir este argumento de palabra clave, funciona igual).

Lo que esto hace es que copia todos los archivos de texto en su directorio de nivel superior o raíz (un nivel más arriba del paquete mypkgque desea distribuir).

¡Espero que esto ayude!

rv.kvetch
fuente
Estaba buscando una manera de no tener que crear un MANIFEST.in, esto funcionó para mí. La última línea también fue crucial para mí. Mis líneas fueroninclude_package_data=False, package_data={ "": ["../CHANGELOG.md"] },
Mendhak
7

Es 2019, y esto es lo que está funcionando: a pesar de los consejos aquí y allá, lo que encontré en Internet medio documentado está utilizando setuptools_scm, pasado como opciones a setuptools.setup. Esto incluirá cualquier archivo de datos que esté versionado en su VCS, ya sea git o cualquier otro, al paquete de la rueda, y hará una "instalación de pip" desde el repositorio de git para llevar esos archivos.

Entonces, acabo de agregar estas dos líneas a la llamada de configuración en "setup.py". Sin instalaciones adicionales o requisitos de importación:

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

No es necesario enumerar manualmente package_data, o en un archivo MANIFEST.in; si está versionado, está incluido en el paquete. Los documentos de "setuptools_scm" ponen énfasis en la creación de un número de versión desde la posición de confirmación, y no tienen en cuenta la parte realmente importante de agregar los archivos de datos. (No me importa si mi archivo de rueda intermedia se llama "* 0.2.2.dev45 + g3495a1f" o usará el número de versión codificado "0.3.0dev0" que he escrito, pero dejo archivos cruciales para el programa trabajar detrás es algo importante)

jsbueno
fuente
7

Paso 1: crea un MANIFEST.inarchivo en la misma carpeta con setup.py

Paso 2: incluya la ruta relativa a los archivos que desea agregarMANIFEST.in

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

Paso 3: configure include_package_data=Truela setup()función para copiar estos archivos en el paquete del sitio

La referencia está aquí.

depuración
fuente
5

En setup.py bajo 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
fuente
1
En realidad, esto no hace nada para lograr el objetivo del OP. Lo que escriba package_datano tendrá influencia sobre lo que setup.py installhace, a menos que modifique el comando de instalación en sí. A menos que esos archivos estén en el directorio del paquete, que generalmente es algo que desea evitar.
wvxvw
3

Aquí hay una respuesta más simple que funcionó para mí.

Primero, según el comentario anterior de Python Dev, no se requieren setuptools:

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

Eso es genial porque poner un requisito de herramientas de configuración en su paquete significa que también tendrá que instalarlo. En breve:

from distutils.core import setup

setup(
    # ...snip...
    packages          = ['pkgname'],
    package_data      = {'pkgname': ['license.txt']},
)
Gringo Suave
fuente
1
Se quejará de que el directorio pkgameno existe
Anthony Kong
1

Solo quería hacer un seguimiento de algo que encontré trabajando con Python 2.7 en Centos 6. Agregar el paquete de datos o archivos de datos como se mencionó anteriormente no funcionó para mí. Agregué un MANIFEST.IN con los archivos que quería que pusieron los archivos que no son de Python en el tarball, pero no los instalé en la máquina de destino a través de RPM.

Al final, pude obtener los archivos en mi solución usando las "opciones" en la configuración / herramientas de configuración. Los archivos de opciones le permiten modificar varias secciones del archivo de especificaciones desde setup.py. Como sigue.

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

archivo - MANIFEST.in:

include license.txt

file - 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
Scott Bowers
fuente
-12

Encontrado una solución: Me cambió el nombre a mi lgpl2.1_license.txta lgpl2.1_license.txt.py, y poner algunas citas triples en todo el texto. Ahora no necesito usar la data_filesopción ni especificar ninguna ruta absoluta. Convertirlo en un módulo de Python es feo, lo sé, pero lo considero menos feo que especificar rutas absolutas.

Ram Rachum
fuente
77
Mira mi post. No tiene que ser feo. Es difícil encontrar un buen ejemplo en la red porque es difícil encontrar buena documentación para configurar paquetes.
Evan Plaice