Frère paquet importations
J'ai essayé de la lecture à travers des questions au sujet de la fratrie des importations et même la
la documentation du paquet, mais je n'ai pas encore trouver de réponse.
Avec la structure suivante:
├── LICENSE.md
├── README.md
├── api
│ ├── __init__.py
│ ├── api.py
│ └── api_key.py
├── examples
│ ├── __init__.py
│ ├── example_one.py
│ └── example_two.py
└── tests
│ ├── __init__.py
│ └── test_one.py
Comment les scripts dans le examples
et tests
répertoires importer à partir de la
api
module et être exécuté depuis la ligne de commande?
Aussi, je voudrais éviter le vilain sys.path.insert
hack pour chaque fichier. Sûrement
cela peut être fait en Python, droit?
- Je vous recommande de sauter au-delà de la
sys.path
hacks et la lecture de la seule solution réelle qui a été posté à ce jour (au bout de 7 ans!). - Par le façon, il ya encore de la place pour une autre bonne solution: la Séparation du code exécutable à partir de la bibliothèque de code; la plupart du temps un script à l'intérieur d'un package ne devrait pas être exécutable pour commencer.
- C'est donc utile, à la fois la question et les réponses. Je suis juste curieux de savoir, comment se fait "a Accepté de Répondre" n'est pas le même que celui qui a reçu la prime dans ce cas?
- C'est une sous-estimé rappel dans ces relative à l'importation d'erreur Q&Comme. J'ai été à la recherche d'un hack tout ce temps, mais au fond je savais qu'il y avait un moyen simple pour la conception de ma manière de contourner le problème. Pour ne pas dire que c'est la solution pour tout le monde ici la lecture, mais c'est un bon rappel qu'il pourrait l'être pour beaucoup.
Vous devez vous connecter pour publier un commentaire.
Sept ans après
Depuis que j'ai écrit la réponse ci-dessous, la modification
sys.path
est toujours rapide et sale truc qui marche bien pour les scripts privés, mais il y a eu plusieurs améliorationssetup.cfg
pour stocker les métadonnées)-m
drapeau et en cours d'exécution comme un paquet fonctionne aussi (mais va tourner un peu gênant si vous voulez convertir votre répertoire de travail dans un paquet installable).sys.path
hacks pour vousDonc, cela dépend vraiment de ce que vous voulez faire. Dans votre cas, puisqu'il semble que votre objectif est de faire un bon paquet à un certain point, de l'installation à travers
pip -e
est probablement votre meilleur pari, même si elle n'est pas encore parfait.Vieille réponse
Comme déjà dit ailleurs, la terrible vérité est que vous avez à faire moche hacks pour permettre les importations de la fratrie des modules ou des parents package à partir d'un
__main__
module. La question est détaillée dans PEP 366. PEP 3122 tenté de manipuler les importations d'une manière plus rationnelle, mais Guido a rejeté un le compte de(ici)
Bien, j'utilise ce modèle sur une base régulière avec
Ici
path[0]
est votre script en cours du dossier parent etdir(path[0])
votre dossier de niveau supérieur.J'ai pas encore été en mesure d'utiliser relatif des importations, ce n'est pas tout, mais il ne permet pas absolue, les importations de haut niveau (dans votre exemple
api
's dossier parent).-m
formulaire ou si vous installez le package (pip et virtualenv le rendre facile)__package__ = "examples"
pour moi. Pourquoi utilisez-vous? 2. Dans quelle situation est__name__ == "__main__"
mais__package__
n'est pasNone
?Fatigué sur sys.chemin des hacks?
Il y a beaucoup de
sys.path.append
-hacks disponibles, mais j'ai trouvé une autre façon de résoudre le problème en main: Le setuptools. Je ne suis pas sûr si il y a des cas limites, qui ne fonctionnent pas bien avec cela. Ce qui suit est testé avec Python 3.6.5, (Anaconda, conda 4.5.1), Windows 10 de la machine.Installation
Le point de départ est la structure du fichier que vous avez fournis, enveloppé dans un dossier appelé
myproject
.Je vais appeler la
.
le dossier racine, et dans mon cas par exemple, il est situé àC:\tmp\test_imports\
.api.py
Comme un cas de test, nous allons utiliser la suite ./api/api.py
test_one.py
Essayez d'exécuter test_one:
Également essayer relative des importations coutume de travail:
À l'aide de
from ..api.api import function_from_api
entraîneraitÉtapes
1) Faire une setup.py le fichier à la racine du répertoire de niveau
Le contenu de la
setup.py
serait*2) l'Utilisation d'un environnement virtuel
Si vous êtes familier avec les environnements virtuels, activer une, et passez à l'étape suivante. L'utilisation d'environnements virtuels ne sont pas absolument nécessaires, mais ils vraiment vous aider dans le long terme (si vous avez plus de 1 projet en cours..). La plupart des étapes de base sont (exécuter dans le dossier racine)
python -m venv venv
source ./venv/bin/activate
(Linux, macOS) ou./venv/Scripts/activate
(Win)Pour en savoir plus à ce sujet, juste Google "à" python virtuel env tutoriel" ou similaire. Vous n'avez probablement jamais besoin de toutes les autres commandes de la création, de l'activation et de la désactivation.
Une fois que vous avez fait et activé un environnement virtuel, votre console devrait donner le nom de l'environnement virtuel dans la parenthèse
et votre arborescence de dossiers devrait ressembler à ceci**
3) pip install votre projet modifiable état
L'installation de votre haut niveau,
myproject
à l'aide depip
. L'astuce est d'utiliser le-e
drapeau lors de l'installation. De cette manière, il est installé modifiable dans un état, et toutes les modifications apportées à l' .py fichiers seront automatiquement inclus dans le paquet installé.Dans le répertoire de racine, exécutez
pip install -e .
(notez le point, il est synonyme de "répertoire courant")Vous pouvez aussi voir qu'il s'est installé à l'aide de
pip freeze
4) Ajouter
myproject.
dans votre importationsNoter que vous devrez ajouter
myproject.
seulement dans les importations qui ne serait pas travailler autrement. Les importations qui ont travaillé sanssetup.py
&pip install
fonctionnera fonctionnent encore correctement. Voir un exemple ci-dessous.Tester la solution
Maintenant, nous allons tester la solution à l'aide de
api.py
défini ci-dessus, ettest_one.py
défini ci-dessous.test_one.py
exécution du test
* Voir le setuptools docs pour plus de commentaires setup.py des exemples.
** En réalité, vous pourriez mettre votre environnement virtuel n'importe où sur votre disque dur.
-e git+https://[email protected]/folder/myproject.git@f65466656XXXXX#egg=myproject
la moindre Idée de comment le résoudre?python setup.py install
ne peuvent pas travailler?setup.py
est nécessaire?setup.py
pour faire de votre code python, un pip-installable paquet python.ModuleNotFoundError
? J'ai installé "myproject" dans un virtualenv suivant ces étapes, et quand j'entre dans une interprétation d'une session et d'exécuterimport myproject
- je obtenirModuleNotFoundError: No module named 'myproject'
?pip list installed | grep myproject
montre qu'il est là, le répertoire est correct, et à la fois la verison depip
etpython
sont vérifiés pour être correct.setup.py
- pour moi, ce était monsrc
répertoire.None {'name': 'MyProject', 'path': None}
. Même classe, lors d'une importation de répertoire courant n'a pas d'imprimer quoi que ce soitpip freeze
Il n'est pas sur la listepip
de commande vers la droitepip
de l'exécutable? Nepython -m pip freeze
donner les mêmes résultats?pip list
montre des paquets, alors qu'pip freeze
montre bizarre noms si installé avec drapeau -eIci est une autre alternative que j'ai insérer dans le haut de l'Python fichiers dans
tests
dossier:..
ici est relatif au répertoire que vous êtes d'exécution à partir de---et non pas le répertoire contenant le test/fichier d'exemple. Je suis en cours d'exécution à partir du répertoire du projet, et j'avais besoin de./
à la place. Espérons que cela aide quelqu'un d'autre.Vous n'avez pas besoin et ne devrait pas hack
sys.path
sauf s'il est nécessaire et dans ce cas, il ne l'est pas. Utilisation:Exécuter à partir du répertoire du projet:
python -m tests.test_one
.Vous devriez probablement se déplacer à
tests
(si ils sont de l'api de unittests) à l'intérieur deapi
et exécuterpython -m api.test
pour exécuter tous les tests (en supposant qu'il est__main__.py
) oupython -m api.test.test_one
pour exécutertest_one
à la place.Vous pouvez également supprimer
__init__.py
deexamples
(il n'est pas un paquet Python) et d'exécuter les exemples dans un virtualenv oùapi
est installé, par exemple,pip install -e .
dans un virtualenv et de l'installer en placeapi
paquet si vous avez bonnesetup.py
.python -m api.test.test_one
à partir de n'importe où lorsque la virtualenv est activé. Si vous ne pouvez pas configurer PyCharm à l'exécution de vos tests, essayez de demander à un nouveau Débordement de Pile question (si vous ne trouvez pas une question existante sur ce sujet).Je n'ai pas encore la compréhension de Pythonology faut voir la manière prévue de partage de code entre les projets indépendants sans un frère ou une sœur ou un parent d'importation hack. Jusqu'à ce jour, c'est ma solution. Pour
examples
outests
pour importer des trucs de..\api
, il ressemblerait à:Pour les frères et sœurs paquet importations, vous pouvez utiliser le insérer ou la ajouter méthode de la [sys.chemin d'accès][2] module:
Ce fonctionne si vous êtes le lancement de vos scripts comme suit:
D'autre part, vous pouvez aussi utiliser le relatif à l'importation:
Dans ce cas, vous devez lancer votre script avec la '-m' argument (notez que, dans ce cas, vous ne devez pas donner l' '.py' extension):
Bien sûr, vous pouvez mélanger les deux approches, de sorte que votre script fonctionne, peu importe comment il s'appelle:
__file__
mondiale, j'ai donc dû utiliser les éléments suivants:sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0]))))
Mais il fonctionne dans n'importe quel répertoire maintenantTLDR
Cette méthode ne nécessite pas de setuptools, chemin de hacks, d'autres arguments de ligne de commande ou de préciser le haut niveau de l'emballage dans chaque fichier de votre projet.
Il suffit de faire un script dans le répertoire parent de ce que vous appelez à votre
__main__
et de tout faire à partir de là. Pour de plus amples explications continuer la lecture.Explication
Ceci peut être accompli sans le piratage d'un nouveau chemin ensemble, en sus de la ligne de commande args, ou l'ajout de code pour chacun de vos programmes de reconnaître de ses frères et sœurs.
La raison de ce qui échoue comme je le crois, a été mentionné auparavant, les programmes ont appelé leurs
__name__
définir comme__main__
. Lorsque cela se produit, le script appelé accepte lui-même pour être au top niveau de l'emballage et refuse de reconnaître les scripts dans des répertoires frère.Cependant, le tout sous le haut niveau de l'annuaire est capable de reconnaître AUTRE CHOSE sous le haut niveau. Cela signifie que le SEULEMENT chose que vous avez à faire pour obtenir des fichiers dans des répertoires frère pour reconnaître/utilisent les uns les autres est de les appeler à partir d'un script dans leur répertoire parent.
Preuve de Concept
Dans un dir avec la structure suivante:
Main.py
contient le code suivant:sib1/call.py contient:
et sib2/callsib.py contient:
Si vous reproduire cet exemple, vous remarquerez que l'appel
Main.py
aura pour résultat Obtenu Appelle" être imprimés tels que définis à l'sib2/callsib.py
même sisib2/callsib.py
ai appelé parsib1/call.py
. Toutefois, si l'on devait appeler directementsib1/call.py
(après avoir fait les changements appropriés pour les importations), il déclenche une exception. Même si elle a fonctionné lorsqu'il est appelé par le script dans son répertoire parent, il ne fonctionnera pas si il croit être sur le haut niveau de l'emballage.J'ai fait un projet d'exemple pour montrer comment j'ai traité ce, qui est en effet un autre sys.chemin de hack comme indiqué ci-dessus. Python Frère Exemple D'Importation, qui s'appuie sur:
if __name__ == '__main__':
import os
import sys
sys.path.append(os.getcwd())
Cela semble être assez efficace, aussi longtemps que votre répertoire de travail reste à la racine du projet Python. Si quelqu'un se déploie ce dans un environnement de production réel ce serait formidable d'entendre si ça fonctionne aussi bien.
Vous avez besoin de regarder pour voir comment l'importer les instructions sont écrites dans le code. Si
examples/example_one.py
utilise l'instruction d'importation suivante:...puis il attend le répertoire racine du projet dans le chemin d'accès système.
La façon la plus simple de soutenir ce sans hacks (comme vous dites), il y a des exemples à partir du répertoire de niveau supérieur, comme ceci:
$ python examples/example.py Traceback (most recent call last): File "examples/example.py", line 3, in <module> from api.api import API ImportError: No module named api.api
. J'ai également le même avecimport api.api
.Juste au cas où quelqu'un en utilisant Pydev sur Eclipse fin ici: vous pouvez ajouter le frère de la mère de chemin d'accès (et donc de l'appel du module parent) comme une bibliothèque externe dossier à l'aide de Projet->Propriétés et réglage Bibliothèques Externes sous le menu de gauche Pydev-PYTHONPATH. Ensuite, vous pouvez importer à partir de votre frère ou votre sœur, e. g.
from sibling import some_class
.Tout d'abord, vous devez éviter d'avoir des fichiers avec le même nom que le module lui-même. Il peut casser les autres importations.
Lorsque vous importez un fichier, d'abord l'interprète vérifie le répertoire courant, puis d'un morceau mondiale répertoires.
À l'intérieur de
examples
outests
vous pouvez appeler le:Traceback (most recent call last): File "example_one.py", line 3, in <module> from ..api import api ValueError: Attempted relative import in non-package
__init__.py
fichier dans le répertoire de niveau supérieur. Sinon, Python ne peut pas le traiter comme un module__name__
est__main__
au lieu depackage.module
, Python ne peut pas voir son parent paquet, de sorte.
points pour rien.