Estou tentando seguir o PEP 328 , com a seguinte estrutura de diretórios:
pkg/
__init__.py
components/
core.py
__init__.py
tests/
core_test.py
__init__.py
Em core_test.py
eu tenho a seguinte declaração de importação
from ..components.core import GameLoopEvents
No entanto, quando executo, recebo o seguinte erro:
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
Pesquisando, encontrei " caminho relativo que não funciona mesmo com __init__.py " e " Importar um módulo de um caminho relativo ", mas eles não ajudaram.
Falta alguma coisa aqui?
python
package
python-import
importerror
init
skytreader
fonte
fonte
unittest
projetos, por isso escrevi este projeto de amostra bastante exaustivo que abrange o aninhamento profundo de módulos, importações relativas e absolutas (onde o trabalho não funciona) e referências relativas e absolutas de dentro de um pacote, bem como importação única, dupla e no nível de pacote. Ajudou as coisas claras até para mim!no module named myimports.foo
quando eu os corro.cd
entrarPyImports
e executarpython -m unittest tests.test_abs
, por exemplo.Respostas:
Sim. Você não o está usando como pacote.
fonte
__init__.py
todo o tempo e os__package__
truques de modificação (descritos abaixo por BrenBarn) necessários para permitir essas importações para scripts executáveis (por exemplo, ao usar um shebang e fazendo./my_script.py
no shell do Unix) tudo seria útil. Toda essa questão foi bastante complicada para eu descobrir ou encontrar documentação concisa e compreensível.pkg
no ponto em que chama esta linha a partir da CLI. Então, deve funcionar como esperado. Se você estiver dentropkg
e ligarpython -m tests.core_test
, não funcionará. Pelo menos não aconteceu comigo.__init__.py
arquivos, mas você continua recebendo oValueError: Attempted relative import in non-package
erro. Eu pagaria um dinheiro muito bom para alguém, em algum lugar, para finalmente explicar em inglês simples como tudo isso funciona.Para elaborar a resposta de Ignacio Vazquez-Abrams :
O mecanismo de importação do Python funciona em relação ao
__name__
arquivo atual. Quando você executa um arquivo diretamente, ele não tem o nome habitual, mas"__main__"
o nome. Portanto, as importações relativas não funcionam.Você pode, como Igancio sugeriu, executá-lo usando a
-m
opção Se você tem uma parte do seu pacote que deve ser executada como um script, também pode usar o__package__
atributo para informar ao arquivo que nome ele deve ter na hierarquia de pacotes.Veja http://www.python.org/dev/peps/pep-0366/ para obter detalhes.
fonte
python -m core_test
de dentro dotests
subdiretório - ele deve ser do pai ou você deve adicionar o pai ao caminho.__package__
para garantir que os arquivos de script executáveis possam importar relativamente outros módulos de dentro do mesmo pacote. Não há como importar relativamente de "todo o sistema". Nem sei por que você faria isso.__package__
símbolo estiver definido como "parent.child", você poderá importar "parent.other_child". Talvez eu não tenha dito isso tão bem.script.py
no pacotepack.subpack
, em seguida, defini-lo é__package__
apack.subpack
vai deixar você fazerfrom ..module import something
a importação algopack.module
. Observe que, como diz a documentação, você ainda precisa ter o pacote de nível superior no caminho do sistema. Já é assim que as coisas funcionam para os módulos importados. A única coisa a__package__
fazer é permitir que você use esse comportamento também para scripts executados diretamente.__package__
no script que é executado diretamente, mas infelizmente, recebo o seguinte erro: "O módulo pai 'xxx' não foi carregado, não pode executar importação relativa"Você pode usar
import components.core
diretamente se anexar o diretório atual asys.path
:fonte
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
Isso também irá trabalharfrom os import sys
olhares como batota :)sys.path
- o pai do diretório do arquivo atual está em.import sys, os.path as path
.import os; os.sys.path.append(os.path.dirname(os.path.abspath('.')))
. Então, um straightimport components.core
funciona para mim, importando do diretório pai do notebook, conforme desejado.Depende de como você deseja iniciar seu script.
Se você deseja iniciar seu UnitTest a partir da linha de comando de maneira clássica, ou seja:
Então, como nesse caso 'componentes' e 'testes' são pastas irmãos, você pode importar o módulo relativo usando a inserção ou o append do módulo sys.path . Algo como:
Caso contrário, você poderá iniciar seu script com o argumento '-m' (observe que, neste caso, estamos falando de um pacote e, portanto, você não deve fornecer a extensão '.py' ), ou seja:
Nesse caso, você pode simplesmente usar a importação relativa como estava fazendo:
Você pode finalmente misturar as duas abordagens, para que seu script funcione, independentemente de como é chamado. Por exemplo:
fonte
python -m pdb myscript.py
para iniciar a sessão de depuração.import pdb; pdb.set_trace()
no código (embutido).insert
vez deappend
? Ou seja,sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
No core_test.py, faça o seguinte:
fonte
Se o seu caso de uso for para executar testes e parecer que é, você poderá fazer o seguinte. Em vez de executar seu script de teste,
python core_test.py
use uma estrutura de teste comopytest
. Em seguida, na linha de comando, você pode inserirIsso executará os testes no seu diretório. Isso contorna a questão do
__name__
ser__main__
que foi apontada pelo @BrenBarn. Em seguida, coloque um__init__.py
arquivo vazio no diretório de teste, isso fará com que o diretório de teste faça parte do seu pacote. Então você será capaz de fazerNo entanto, se você executar o script de teste como um programa principal, as coisas irão falhar novamente. Então, basta usar o corredor de teste. Talvez isso também funcione com outros corredores de teste, como
nosetests
mas não o verifiquei. Espero que isto ajude.fonte
Minha solução rápida é adicionar o diretório ao caminho:
fonte
O problema está no seu método de teste,
você tentou
python core_test.py
você receberá este erro ValueError: tentativa de importação relativa em pacote não
Razão: você está testando sua embalagem a partir de fontes que não são de pacote.
então teste seu módulo a partir da fonte do pacote.
se essa é a estrutura do seu projeto,
cd pkg
ou de fora do pacote /
único
.
se você deseja importar da pasta no mesmo diretório. para cada passo atrás, adicione mais um.no
how.py
caso você queira importar como do hello.py
fonte
from .. import how
, como você importa uma classe / método específico do arquivo 'como'. quando faço o equivalente afrom ..how import foo
, recebo "tentativa de importação relativa além do pacote de nível superior"Fio antigo. Descobri que adicionar um arquivo
__all__= ['submodule', ...]
ao __init__.py e depois usá-lofrom <CURRENT_MODULE> import *
no destino funciona bem.fonte
Você pode usar
from pkg.components.core import GameLoopEvents
, por exemplo, uso pycharm, a seguir é minha imagem da estrutura do projeto, apenas importo a partir do pacote raiz e funciona:fonte
Como Paolo disse, temos 2 métodos de chamada:
Uma diferença entre eles é sys.path [0] string. Como a interpretação pesquisará sys.path ao importar , podemos fazer com
tests/core_test.py
:E mais, depois disso, podemos executar o core_test.py com outros métodos:
Observe que o py36 foi testado apenas.
fonte
Essa abordagem funcionou para mim e é menos confusa do que algumas soluções:
O diretório pai está no meu PYTHONPATH e há
__init__.py
arquivos no diretório pai e neste diretório.O exemplo acima sempre funcionou no python 2, mas o python 3 às vezes atinge um ImportError ou ModuleNotFoundError (o último é novo no python 3.6 e uma subclasse de ImportError), portanto, o seguinte ajuste funciona para mim no python 2 e 3:
fonte
Tente isto
fonte
Se alguém está procurando uma solução alternativa, eu me deparei com uma. Aqui está um pouco de contexto. Eu queria testar um dos métodos que tenho em um arquivo. Quando eu corro de dentro
sempre reclamou das importações relativas. Tentei aplicar as soluções acima, mas não funcionou, pois havia muitos arquivos aninhados, cada um com várias importações.
Aqui está o que eu fiz. Acabei de criar um lançador, um programa externo que importaria os métodos necessários e os chamaria. Embora não seja uma ótima solução, ele funciona.
fonte
Aqui está uma maneira de irritar todos, mas funciona muito bem. Nos testes executados:
Em seguida, basta importar componentes como faria normalmente.
fonte
Isso é muito confuso, e se você estiver usando IDE como pycharm, é um pouco mais confuso. O que funcionou para mim: 1. Faça as configurações do projeto pycharm (se você estiver executando o python a partir de um VE ou do diretório python) 2. Não há nada errado da maneira que você definiu. em algum momento ele funciona com a classe de importação folder1.file1
se não funcionar, use import folder1.file1 3. Sua variável de ambiente deve ser mencionada corretamente no sistema ou fornecê-la no seu argumento de linha de comando.
fonte
Como seu código contém
if __name__ == "__main__"
, que não é importado como um pacote, é melhor usá-losys.path.append()
para resolver o problema.fonte
if __name__ == "__main__"
em seu arquivo faça diferença em qualquer coisa relacionada à importação.