J'essaie de suivre PEP 328 , avec la structure de répertoires suivante:
pkg/
__init__.py
components/
core.py
__init__.py
tests/
core_test.py
__init__.py
Dans core_test.py
J'ai la déclaration d'importation suivante
from ..components.core import GameLoopEvents
Cependant, lorsque je cours, j'obtiens l'erreur suivante:
tests$ python core_test.py
Traceback (most recent call last):
File "core_test.py", line 3, in <module>
from ..components.core import GameLoopEvents
ValueError: Attempted relative import in non-package
En cherchant, j'ai trouvé "le chemin relatif ne fonctionnait même pas avec __init__.py " et " Importer un module à partir d'un chemin relatif " mais ils n'ont pas aidé.
Y a-t-il quelque chose qui me manque ici?
python
package
python-import
importerror
init
skytreader
la source
la source
unittest
projets, j'ai donc écrit cet exemple de projet assez exhaustif qui couvre l'imbrication profonde des modules, les importations relatives et absolues (où le travail et non), et le référencement relatif et absolu à l'intérieur d'un package, ainsi que l'importation de classes au niveau simple, double et package. A aidé les choses claires jusqu'à pour moi!no module named myimports.foo
quand je les exécute.cd
dansPyImports
et exécutéepython -m unittest tests.test_abs
, par exemple.Réponses:
Oui. Vous ne l'utilisez pas en tant que package.
la source
__init__.py
tout le chemin et la__package__
ruse de modification (décrite ci-dessous par BrenBarn) nécessaire pour autoriser ces importations pour les scripts exécutables (par exemple lors de l'utilisation d'un shebang et faire./my_script.py
au shell Unix) seraient tous utiles. Toute cette question était assez difficile pour moi de comprendre ou de trouver une documentation concise et compréhensible sur.pkg
au point où vous appelez cette ligne à partir de la CLI. Ensuite, cela devrait fonctionner comme prévu. Si vous êtes à l'intérieurpkg
et que vous appelezpython -m tests.core_test
, cela ne fonctionnera pas. Au moins, ce n'était pas pour moi.__init__.py
fichiers mais vous continuez à recevoir l'ValueError: Attempted relative import in non-package
erreur. Je paierais vraiment très bien pour quelqu'un, quelque part, pour enfin expliquer en anglais simple comment tout cela fonctionne.Pour développer la réponse d' Ignacio Vazquez-Abrams :
Le mécanisme d'importation Python fonctionne par rapport à celui
__name__
du fichier actuel. Lorsque vous exécutez un fichier directement, il n'a pas son nom habituel, mais a"__main__"
plutôt son nom. Les importations relatives ne fonctionnent donc pas.Vous pouvez, comme l'a suggéré Igancio, l'exécuter en utilisant l'
-m
option. Si vous avez une partie de votre package qui doit être exécutée en tant que script, vous pouvez également utiliser l'__package__
attribut pour indiquer à ce fichier le nom qu'il est censé avoir dans la hiérarchie des packages.Voir http://www.python.org/dev/peps/pep-0366/ pour plus de détails.
la source
python -m core_test
partir dutests
sous-répertoire - cela doit provenir du parent, ou vous devez ajouter le parent au chemin.__package__
pour vous assurer que les fichiers de script exécutables peuvent importer relativement d'autres modules à partir du même package. Il n'y a aucun moyen d'importer relativement de "l'ensemble du système". Je ne sais même pas pourquoi tu voudrais faire ça.__package__
symbole est défini sur "parent.child", vous pourrez alors importer "parent.other_child". Peut-être que je ne l'ai pas si bien formulé.script.py
dans le packagepack.subpack
, puis en définissant il est__package__
àpack.subpack
vous laisser fairefrom ..module import something
quelque chose de l'importationpack.module
. Notez que, comme le dit la documentation, vous devez toujours avoir le package de niveau supérieur sur le chemin du système. C'est déjà ainsi que les choses fonctionnent pour les modules importés. La seule chose à__package__
faire est de vous laisser utiliser ce comportement également pour les scripts exécutés directement.__package__
dans le script qui est exécuté directement mais malheureusement, j'obtiens l'erreur suivante: "Le module parent 'xxx' n'est pas chargé, ne peut pas effectuer d'importation relative"Vous pouvez utiliser
import components.core
directement si vous ajoutez le répertoire actuel àsys.path
:la source
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
cela fonctionnera égalementfrom os import sys
ressemble à de la triche :)sys.path
- le parent du répertoire dansimport sys, os.path as path
.import os; os.sys.path.append(os.path.dirname(os.path.abspath('.')))
. Ensuite, une ligne droiteimport components.core
fonctionne pour moi, en important à partir du répertoire parent du bloc-notes comme vous le souhaitez.Cela dépend de la façon dont vous souhaitez lancer votre script.
Si vous souhaitez lancer votre UnitTest à partir de la ligne de commande de manière classique, c'est:
Ensuite, comme dans ce cas, les «composants» et les «tests» sont des dossiers de frères et sœurs, vous pouvez importer le module relatif en utilisant l' insertion ou la méthode d' ajout du module sys.path . Quelque chose comme:
Sinon, vous pouvez lancer votre script avec l'argument '-m' (notez que dans ce cas, nous parlons d'un paquet, et donc vous ne devez pas donner l' extension '.py' ), c'est-à-dire:
Dans un tel cas, vous pouvez simplement utiliser l'importation relative comme vous le faisiez:
Vous pouvez enfin mélanger les deux approches, afin que votre script fonctionne quel que soit son nom. Par exemple:
la source
python -m pdb myscript.py
pour lancer la session de débogage.import pdb; pdb.set_trace()
dans le code (en ligne).insert
place deappend
? Autrement dit,sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
Dans core_test.py, procédez comme suit:
la source
Si votre cas d'utilisation est destiné à l'exécution de tests et qu'il semble que c'est le cas, vous pouvez effectuer les opérations suivantes. Au lieu d'exécuter votre script de test,
python core_test.py
utilisez un framework de test tel quepytest
. Ensuite, sur la ligne de commande, vous pouvez entrerCela exécutera les tests dans votre répertoire. Cela permet de contourner le problème de l'
__name__
être__main__
qui a été souligné par @BrenBarn. Ensuite, placez un__init__.py
fichier vide dans votre répertoire de test, cela fera du répertoire de test une partie de votre package. Ensuite, vous pourrez faireCependant, si vous exécutez votre script de test en tant que programme principal, les choses échoueront à nouveau. Il suffit donc d'utiliser le lanceur de test. Peut-être que cela fonctionne également avec d'autres coureurs de test tels que
nosetests
mais je ne l'ai pas vérifié. J'espère que cela t'aides.la source
Ma solution rapide consiste à ajouter le répertoire au chemin:
la source
Le problème vient de votre méthode de test,
tu as essayé
python core_test.py
alors vous obtiendrez cette erreur ValueError: tentative d'importation relative dans un non-package
Raison: vous testez votre emballage à partir d'une source hors emballage.
testez donc votre module à partir de la source du package.
s'il s'agit de la structure de votre projet,
cd pkg
ou de l'extérieur pkg /
unique
.
si vous souhaitez importer à partir du dossier dans le même répertoire. pour chaque pas en arrière, ajoutez un de plus.dans
how.py
incase si vous voulez importer comment de hello.py
la source
from .. import how
, comment importez-vous une classe / méthode spécifique à partir du fichier 'how'. quand je fais l'équivalent,from ..how import foo
je reçois "tentative d'importation relative au-delà du package de niveau supérieur"Vieux fil. J'ai découvert que l'ajout d'un
__all__= ['submodule', ...]
au fichier __init__.py , puis l'utilisation defrom <CURRENT_MODULE> import *
dans la cible fonctionne correctement .la source
Vous pouvez utiliser
from pkg.components.core import GameLoopEvents
, par exemple, j'utilise pycharm, l'image ci-dessous est la structure de mon projet, je viens d'importer à partir du package racine, puis cela fonctionne:la source
Comme l'a dit Paolo , nous avons 2 méthodes d'invocation:
Une différence entre eux est la chaîne sys.path [0]. Puisque l'interprète recherchera sys.path lors de l'importation , nous pouvons faire avec
tests/core_test.py
:Et plus après cela, nous pouvons exécuter core_test.py avec d'autres méthodes:
Remarque, py36 testé uniquement.
la source
Cette approche a fonctionné pour moi et est moins encombrée que certaines solutions:
Le répertoire parent est dans mon PYTHONPATH, et il y a des
__init__.py
fichiers dans le répertoire parent et ce répertoire.Ce qui précède a toujours fonctionné en python 2, mais python 3 a parfois frappé une ImportError ou ModuleNotFoundError (ce dernier est nouveau en python 3.6 et une sous-classe d'ImportError), donc le réglage suivant fonctionne pour moi dans les deux python 2 et 3:
la source
Essaye ça
la source
Si quelqu'un cherche une solution de contournement, je suis tombé sur un. Voici un peu de contexte. Je voulais tester l'une des méthodes que j'ai dans un fichier. Quand je l'exécute de l'intérieur
il se plaignait toujours des importations relatives. J'ai essayé d'appliquer les solutions ci-dessus, mais j'ai échoué, car il y avait de nombreux fichiers imbriqués, chacun avec plusieurs importations.
Voici ce que j'ai fait. Je viens de créer un lanceur, un programme externe qui importerait les méthodes nécessaires et les appellerait. Bien que ce ne soit pas une excellente solution, cela fonctionne.
la source
Voici une façon qui va énerver tout le monde mais qui fonctionne plutôt bien. Dans les tests exécutés:
Il vous suffit ensuite d'importer des composants comme vous le feriez normalement.
la source
C'est très déroutant, et si vous utilisez IDE comme pycharm, c'est un peu plus déroutant. Ce qui a fonctionné pour moi: 1. Définissez les paramètres du projet pycharm (si vous exécutez python à partir d'un VE ou d'un répertoire python) 2. Il n'y a pas de mal à la façon dont vous avez défini. parfois, il fonctionne avec la classe d'importation de folder1.file1
si cela ne fonctionne pas, utilisez import folder1.file1 3. Votre variable d'environnement doit être correctement mentionnée dans le système ou fournissez-la dans votre argument de ligne de commande.
la source
Parce que votre code contient
if __name__ == "__main__"
, qui n'est pas importé en tant que package, vous feriez mieux de l'utilisersys.path.append()
pour résoudre le problème.la source
if __name__ == "__main__"
dans votre fichier fasse une différence pour tout ce qui concerne l'importation.