56 votes

Comment configurer __main__.py, __init__.py et __setup__.py pour une configuration de paquet de base?

Arrière-plan:

J'ai une structure de répertoire comme suit:

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

Je veux être en mesure d'exécuter le code dans un grand nombre de façons différentes.

  1. pip install Package puis python puis from Package import *

  2. python -m Package qui devrait faire la chose en __main__.py

  3. python __main__.py qui devrait également faire la chose en __main__.py mais cette fois, nous supposons que vous avez téléchargé la source plutôt que d' pip installing.

Maintenant, j'ai pris les deux premiers de travail, mais avec un désordre de l'installation:

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 .......

Ce qui ferait plus de sens pour moi, ce serait dans les deux cas à écrire

from code import ........

mais ça me donne des erreurs d'importation.

Question:

Est la façon dont je l'ai vraiment la seule façon?

Et surtout, comment puis-je soutenir le troisième cas d'utilisation? Maintenant, python __main__.py jette

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

Notes:

J'ai lu

47voto

a_guest Points 5059

Vous avez presque tout ce dont vous avez besoin (même un peu plus)! J'irais avec la configuration suivante:

code.py:

foo = 1

__init__.py:

from .code import foo

Faire un par rapport à l'importation ici, car __init__.py sera utilisé lors de l'importation de l'ensemble du paquet. Notez que nous marquer explicitement les importer en tant que parent par l'aide de la .-syntaxe, car cela est nécessaire pour Python 3 (et en Python 2 si vous n'avez from __future__ import absolute_import).

__main__.py:

from Package import foo

print('foo = ', foo)

C'est le script principal, et nous utilisons donc une absolue import déclaration. Ce faisant, nous supposons que le paquet a été installé (ou au moins a été mis sur le chemin); et c'est la façon dont les paquets doivent être traitées avec! Vous pourriez penser que cela est en conflit avec votre troisième cas d'utilisation, mais en fait il n'y a pas de raison de pas à pip install face à un package. Et il n'est vraiment pas une grosse affaire (en particulier lors de l'utilisation d'un virtualenv)!

Si votre préoccupation est de bricoler avec les fichiers source et facilement observer les changements par rapport à l'exécution de l' __main__.py le fichier, alors vous pouvez simplement installer le paquet à l'aide de l' -e ("modifiable") commutateur: pip install -e . (en supposant que vous êtes dans le répertoire Package). Avec votre structure de répertoire en cours, cependant, cela ne fonctionne pas parce que l' -e switch place un egg-link dans le répertoire contenant l' setup.py le dossier; ce répertoire ne contient pas de paquetage nommé Package mais src à la place (j'ai une question à ce sujet).

Au lieu de cela, si vous suivez la convention pour nommer le répertoire racine d'une source du paquet après que le paquet lui-même (c'est - Package de votre exemple) puis de l'installation avec des -e n'est pas un problème: Python n'trouver le package requis Package dans le répertoire correspondant:

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

Ceci vous permet également d'omettre la meilleure définition de l' package_dir={'Package': 'src'} en setup.py.

Une remarque à propos de setup.py: Pour les trois cas d'utilisation qui vous avez spécifié, il n'est pas nécessaire de définir un point d'entrée. C'est que vous pouvez sauter la ligne entry_points={ 'console_scripts': ['Package = src.__main__:main' ] }. Par l'envoi d'un __main__.py module python -m Package sera facilement exécuter du code dans ce module. Vous pouvez également ajouter un supplémentaire si la clause:

def main():
    print('foo = ', foo)

if __name__ == '__main__':
    main()

Le point d'entrée sur l'autre main vous permet d'exécuter directement le code en __main__.main de la CLI; qui est en cours d'exécution $ Package va exécuter le code correspondant.

Recap

La ligne de fond est que je serais toujours utiliser pip install lorsqu'ils traitent avec des paquets. Et pourquoi pas, surtout si vous avez déjà créé un setup.py le fichier? Si des modifications de l'emballage doivent être appliqués "en temps réel", alors vous pouvez l'installer avec l' -e de l'interrupteur (ce qui peut nécessiter un changement de nom de l' src le dossier, voir ci-dessus). Donc votre troisième cas d'utilisation se lire comme "Télécharger la source et l' pip install (-e) Package (dans un virtualenv); ensuite, vous pouvez exécuter python __main__.py".


Modifier

Exécutez __main__.py sans pip install

Si vous ne souhaitez pas installer le paquet via pip, mais encore être en mesure d'exécuter l' __main__.py script, j'avais toujours aller avec la configuration ci-dessus. Ensuite, nous devons faire en sorte que l' from Package import ... déclaration(s) sont toujours réussir et cela peut être réalisé par l'extension du chemin d'importation (à noter que cela nécessite l' src annuaire être renommé le nom du paquet!).

Modifier PYTHONPATH

Pour Linux, bash, vous pouvez définir le Pythonpath comme suit:

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

Ou si vous êtes dans le même répertoire que __main__.py:

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

Bien sûr, il existe différentes manières pour différents systèmes d'exploitation.

Étendre le chemin de __main__.py

Vous (ou plutôt votre collègue) pourrait ajouter les lignes suivantes en haut du script (avant l' from Package import ... des déclarations):

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

Étendre le chemin de sitecustomize.py

Vous pouvez placer un module nommé sitecustomize.py dans la lib/python3.5/site-packages/ répertoire de votre installation de Python qui contient les lignes suivantes:

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

La création d'un haut niveau, main.py script

Donc, si vous voulez avoir la présentation suivante:

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

main.py contient

import src.__main__

Maintenant, __main__.py est traitée comme une partie de l' src paquet et le rapport de l'importation travail. Au lieu de courir python src/__main__.py vous exécutez python main.py maintenant.

3voto

MatTheWhale Points 661

from code import ......... échoue car il n'y a pas de paquet Python installé sur votre système nommé code. Il y a un Python module sur votre système nommé code, mais dans votre instruction import vous ne spécifiez pas le package de votre code module peuvent être trouvés dans.

Le but de l' __init__.py le fichier que vous avez en src/ indique à Python que l' src/ répertoire doit être traité un paquet Python, avec son contenu, comme les modules à l'intérieur de l'emballage. Depuis code.py se trouve dans src/ avec votre __init__.py fichier code module se trouve dans votre src package.

Maintenant que vous savez quel package de votre code module peut être trouvée, vous pouvez importer des trucs de avec:

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

Aussi, comme une note de côté: L' __init__.py fait son travail tout en étant présent dans votre src/ répertoire, de sorte qu'il n'a même pas besoin de contenir tout le code. Pour cette raison, il est généralement une bonne idée de laisser l' __init__.py le fichier vide.

1voto

Gribouillis Points 1808

J'utilise souvent cette configuration parce qu'elle fonctionne mieux avec python setup.py develop

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

Ce n'est probablement pas (encore) la réponse détaillée que vous attendez, mais je pense que cela vaut la peine d'essayer pour les trois cas d'utilisation.

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X