mac python pip setuptools

python - mac - ¿Cómo configurar__main__.py,__init__.py y__setup__.py para una configuración de paquete básica?



pip install python 3 (3)

A menudo utilizo esta configuración porque funciona mejor con python setup.py develop

Package_root/ setup.py src/ Package/ __init__.py __main__.py code.py

Probablemente no sea (aún) la respuesta detallada que está esperando, pero creo que vale la pena probar los tres casos de uso.

setup( ... package_dir = {'''': ''src''}, entry_points = {''console_scripts'': [''Package = Package.__main__:main''],}, packages = find_packages(exclude=["Package.egg_info",]), ...)

Fondo:

Tengo una estructura de directorio así:

Package/ setup.py src/ __init__.py __main__.py code.py

Quiero poder ejecutar el código de muchas maneras diferentes.

  1. pip install Package y luego python y luego from Package import *

  2. python -m Package que debería hacer la cosa en __main__.py

  3. python __main__.py que también debería hacer lo que está en __main__.py pero esta vez, asumimos que ha descargado la fuente en lugar de la pip installing .

Ahora tengo los dos primeros para trabajar, pero con una configuración desordenada:

setup.py:

setup( name=''Package'', packages=[''Package''], package_dir={''Package'': ''src''}, ... entry_points={ ''console_scripts'': [''Package = src.__main__:main'' ] }

__init__.py:

from Package.code import .......

__main__.py:

from . import .......

Lo que tendría más sentido para mí sería en ambos casos escribir.

from code import ........

Pero eso me da errores de importación.

Pregunta:

¿Es la forma en que lo tengo realmente la única manera?

Y lo más importante, ¿cómo apoyo el tercer caso de uso? En este momento, python __main__.py lanza

File "__main__.py", line 10, in <module> from . import code ImportError: cannot import name ''class defined in code.py''

Notas:

he leído


Tienes casi todo lo que necesitas (incluso un poco más)! Yo iría con la siguiente configuración:

code.py :

foo = 1

__init__.py:

from .code import foo

Haciendo una importación relativa aquí porque __init__.py se usará al importar el paquete completo. Tenga en cuenta que marcamos explícitamente la importación como relativa mediante el uso de . -sintaxis porque esto es necesario para Python 3 (y en Python 2 si lo hizo from __future__ import absolute_import ).

__main__.py:

from Package import foo print(''foo = '', foo)

Este es el script principal del paquete, por lo que usamos una declaración de import absoluta. Al hacerlo, asumimos que el paquete se ha instalado (o al menos se ha colocado en la ruta); ¡Y esa es la forma en que se deben tratar los paquetes! Podría pensar que esto está en conflicto con su tercer caso de uso, pero en realidad no hay razón para no pip install cuando se trata de un paquete. ¡Y realmente no es un gran problema (especialmente cuando se usa un virtualenv )!

Si su preocupación es jugar con los archivos de origen y observar fácilmente los cambios ejecutando el archivo __main__.py , simplemente puede instalar el paquete utilizando el interruptor -e ("editable"): pip install -e . (asumiendo que usted está en el Package directorio). Sin embargo, con su estructura de directorios actual, esto no funcionará porque el -e colocará un egg-link al directorio que contiene el archivo setup.py ; este directorio no contiene un paquete llamado Package sino src (tengo una pregunta al respecto ).

En cambio, si sigue la convención para nombrar el directorio raíz de la fuente de un paquete después del mismo paquete (que es el Package para su ejemplo), entonces la instalación con -e no es un problema: Python encuentra el paquete requerido en el directorio correspondiente:

$ tree Package/ Package/ ├── setup.py └── Package <-- Renamed "src" to "Package" because that''s the package''s name. ├── code.py ├── __init__.py └── __main__.py

Esto también le permite omitir la definición adicional de package_dir={''Package'': ''src''} en setup.py .

Una nota sobre setup.py : Para los tres casos de uso que ha especificado, no es necesario definir un punto de entrada. Es decir, puede omitir la línea entry_points={ ''console_scripts'': [''Package = src.__main__:main'' ] } . Al enviar un módulo __main__.py , el python -m Package ejecutará fácilmente el código en este módulo. También puede agregar una cláusula if adicional:

def main(): print(''foo = '', foo) if __name__ == ''__main__'': main()

Por otro lado, el punto de entrada le permite ejecutar directamente el código en __main__.main desde la CLI; que está ejecutando $ Package ejecutará el código correspondiente.

Resumen

La conclusión es que siempre usaría pip install cuando manejaba paquetes. ¿Y por qué no, especialmente si ya ha creado un archivo setup.py ? Si los cambios en el paquete se deben aplicar "en tiempo real", entonces puede instalar con el -e (esto podría requerir un cambio de nombre de la carpeta src , ver más arriba). Por lo tanto, su tercer caso de uso se leería como "Descargue el pip install (-e) Package source- pip install (-e) Package (dentro de un virtualenv); luego puede ejecutar python __main__.py ".

Editar

Ejecuta __main__.py sin pip install

Si no desea instalar el paquete a través de pip pero aún puede ejecutar el script __main__.py , seguiría con la configuración anterior. Luego, debemos asegurarnos de que la (s) declaración (es) de from Package import ... aún tengan éxito y esto se puede lograr extendiendo la ruta de importación (tenga en cuenta que esto requiere que el directorio src sea ​​renombrado al nombre del paquete).

Modificar PYTHONPATH

Para Linux bash puede configurar el Pythonpath de la siguiente manera:

export PYTHONPATH=$PYTHONPATH:/path/to/Package

O si estás en el mismo directorio que __main__.py :

export PYTHONPATH=$PYTHONPATH:`cd ..; pwd`

Por supuesto, hay diferentes formas para diferentes sistemas operativos.

Extender la ruta en __main__.py

Usted (o, más bien, su colega) podría agregar las siguientes líneas a la parte superior de la secuencia de comandos (antes de las declaraciones from Package import ... ):

import sys sys.path.append(''/path/to/Package'')

Extender el camino en sitecustomize.py

Puede colocar un módulo llamado sitecustomize.py en el sitecustomize.py lib/python3.5/site-packages/ de su instalación de Python que contiene las siguientes líneas:

import sys sys.path.append(''/path/to/Package'')

Crear un script main.py nivel superior main.py

Entonces tendrías el siguiente diseño:

$ tree Package/ Package/ ├── main.py <-- Add this file. ├── setup.py └── src ├── code.py ├── __init__.py └── __main__.py

donde main.py contiene

import src.__main__

Ahora __main__.py se trata como parte del paquete src y la importación relativa funcionará. En lugar de ejecutar python src/__main__.py , ahora ejecutaría python main.py


from code import ......... falla porque no hay ningún paquete de Python instalado en su sistema llamado code . Hay un módulo Python en su sistema llamado code , pero en su declaración de importación no especifica el paquete en el que se puede encontrar su módulo de code .

El propósito del archivo __init__.py que tiene en src/ le dice a Python que el directorio src/ debe tratarse como un paquete de Python, con su contenido como los módulos dentro del paquete. Dado que code.py se encuentra en src/ junto con su archivo __init__.py , su módulo de code se encuentra en su paquete src .

Ahora que sabe en qué paquete se puede encontrar su módulo de code , puede importar cosas de él con:

from src.code import .........

Además, como nota al margen: el __init__.py hace su trabajo solo por estar presente en su directorio src/ , por lo que ni siquiera necesita contener ningún código. Por esa razón, generalmente es una buena idea dejar el archivo __init__.py blanco.