Copie el archivo de configuración en la instalación

Estoy tratando de empaquetar mi proyecto Python, que viene con un archivo de configuración de puntos que quiero copiar en el directorio de inicio del usuario durante la instalación. La guía rápida para empaquetar dice que esto se puede hacer usando el argumento setuptools.setup para setuptools.setup . Así que esto es lo que tengo:

 data_files = [(os.path.expanduser("~"), [".my_config"])] 

Esto parece funcionar bien si uso python setup.py install , pero cuando python setup.py install mi paquete a PyPI y ejecuto pip install el python setup.py install no se copia.

FWIW, puse el archivo de puntos en MANIFEST.in y también intenté incluir el argumento package_data en la setup . Ninguno de estos pasos parece hacer una diferencia. Si pip install y hurgue en el directorio de site-packages , aquí están los archivos de origen.

¿Cómo puedo lograr lo que estoy buscando?

Este es un problema que tuve una vez para experimentarme. Su raíz es que cuando está creando un archivo de rueda, todas las rutas absolutas especificadas en los data_files de data_files se relativizarán en el directorio de site-packages destino, vea este problema en github . Esto influye en las instalaciones realizadas por pip install ya que construirá una rueda a partir de cualquier paquete fuente ( .tar.gz , .tar.bz2 o .zip ) e instalará la rueda resultante:

 $ pip install spam-0.1.tar.gz Processing ./spam-0.1.tar.gz Building wheels for collected packages: spam Running setup.py bdist_wheel for spam ... done Stored in directory: /Users/hoefling/Library/Caches/pip/wheels/d0/95/be/bc79f1d589d90d67139481a3e706bcc54578fdbf891aef75c0 Successfully built spam Installing collected packages: spam Successfully installed spam-0.1 

Comprobando los rendimientos de archivos instalados:

 $ pip show -f spam Name: spam Version: 0.1 ... Location: /Users/hoefling/.virtualenvs/stackoverflow/lib/python3.6/site-packages Requires: Files: Users/hoefling/.my_config spam-0.1.dist-info/DESCRIPTION.rst spam-0.1.dist-info/INSTALLER spam-0.1.dist-info/METADATA spam-0.1.dist-info/RECORD spam-0.1.dist-info/WHEEL spam-0.1.dist-info/metadata.json spam-0.1.dist-info/top_level.txt 

Observe que la ruta que se pretende que sea absoluta es relativa al directorio de Location En el ejemplo, .my_config se colocaría en /Users/hoefling/.virtualenvs/stackoverflow/lib/python3.6/site-packages/Users/hoefling/.my_config .

Se pone aún mejor porque estas ruedas construidas se almacenan en caché en su disco, por lo que la próxima vez que reinstale el paquete y la rueda incorporada aún exista en la caché de pip , se usará para la instalación y ni siquiera verá ninguna mención de Construyendo una rueda en el registro de la terminal.

No hay una solución real para evitar esto. La solución más decente que encontré es prohibir los paquetes “binarios” al instalar para hacer cumplir la ejecución del setup.py del paquete en la instalación:

 $ pip install spam-0.1.tar.gz --no-binary=spam Processing ./spam-0.1.tar.gz Skipping bdist_wheel for spam, due to binaries being disabled for it. Installing collected packages: spam Running setup.py install for spam ... done Successfully installed spam-0.1 

El archivo ahora está colocado correctamente:

 $ pip show -f spam Name: spam Version: 0.1 ... Location: /Users/hoefling/.virtualenvs/stackoverflow/lib/python3.6/site-packages Requires: Files: ../../../../../.my_config spam-0.1-py3.6.egg-info/PKG-INFO spam-0.1-py3.6.egg-info/SOURCES.txt spam-0.1-py3.6.egg-info/dependency_links.txt spam-0.1-py3.6.egg-info/top_level.txt 

Desafortunadamente, el usuario debe ser informado por separado acerca de cómo llamar a pip install con la tecla adicional (a través de Léame, Preguntas frecuentes de la página web o similar), ya que no hay posibilidad de prohibir la construcción de la rueda en los metadatos del paquete.

Como resultado, ya no incluyo archivos con rutas absolutas. En su lugar, los instalo con las fonts de python en el directorio de site-packages . En el código de Python, tengo que agregar lógica adicional para las comprobaciones de existencia y la copia de archivos si es necesario:

 # program entrypoint if __name__ == '__main__': config = os.path.join(os.path.expanduser('~'), '.my_config') if not os.path.exists(config): shutil.copyfile('.my_config', config) main.run() 

Además de lo que dijo @hoefling, ¡te sugiero que no data_files en absoluto! Porque es realmente impredecible donde se copiarán los archivos. Puede probar esto dando al directorio algo como '' , '/' o '/anything/you/want' .

Le sugiero que use package_data lugar, que simplemente copia los archivos en la raíz del paquete distribuido en la instalación. Luego, puede copiarlo en cualquier lugar que desee en el tiempo de ejecución.

Para obtener más información sobre package_data , consulte el Doc Python https://docs.python.org/2/distutils/setupscript.html#installing-package-data