Se um arquivo Python dentro de um pacote gera ImportError: attempted relative import with no known parent package quando você o executa diretamente, inicie-o como um módulo a partir do diretório acima do pacote. No macOS, mude para a raiz do projeto no Terminal e execute python3 -m package.module. Isso fornece ao Python o contexto de pacote que um caminho de arquivo direto não fornece.
Por que uma importação relativa falha quando executo um arquivo diretamente?
Uma importação relativa como from .helpers import format_result depende do nome do pacote do módulo. O Python usa pontos iniciais para identificar o pacote atual e seus pais. Quando você executa um arquivo com python3 path/to/file.py, o Python executa esse arquivo como o módulo de nível superior __main__; ele não fornece a ele os metadados de pacote necessários para resolver o ponto. Portanto, a importação pode falhar mesmo que helpers.py esteja ao lado do arquivo. A referência de importação do Python descreve importações relativas e o tratamento especial de __main__.
Por exemplo, suponha que seu projeto esteja organizado assim:
project/
└── myapp/
├── __init__.py
├── cli.py
└── helpers.py
Se cli.py contiver from .helpers import format_result, executar python3 myapp/cli.py a partir de project é a invocação errada. O problema é como o Python foi solicitado a executar o arquivo, não necessariamente que a importação relativa ou a localização do arquivo estejam erradas.
Execute o módulo a partir da raiz do projeto no macOS
Abra o Terminal e mude para o diretório que contém myapp. Substitua o caminho de exemplo pela localização real do seu projeto. Coloque o caminho entre aspas para que os espaços sejam tratados pelo shell; o shell padrão do Terminal do macOS é o zsh. O guia do Terminal da Apple documenta esse padrão.
cd "/Users/yourname/Projects/project"
python3 -m myapp.cli
A opção -m solicita ao Python que encontre e execute o módulo pelo seu nome de pacote com pontos. Mantenha o nome do pacote no comando e omita .py e barras do sistema de arquivos. O caminho de busca de módulos do Python começa com o diretório atual quando invocado com -m, e é por isso que executar a partir da raiz do projeto é importante. A documentação de sys.path do Python explica esse caminho inicial.
Se o comando usar a instalação errada do Python, ative primeiro o ambiente virtual existente do projeto e, em seguida, use o mesmo padrão python -m. Para um .venv típico na raiz do projeto:
cd "/Users/yourname/Projects/project"
source .venv/bin/activate
python -m myapp.cli
Usar python após a ativação seleciona o interpretador do ambiente. Se o seu ambiente tiver um nome ou local diferente, ajuste o caminho de ativação; não presuma que .venv existe. Você pode verificar o interpretador selecionado com python -c 'import sys; print(sys.executable)'.
Se você quiser executar o pacote em si
Para um comando destinado a iniciar o pacote, adicione __main__.py dentro do pacote e mantenha o ponto de entrada enxuto:
myapp/
├── __init__.py
├── __main__.py
├── cli.py
└── helpers.py
Em myapp/__main__.py, importe e chame a função de entrada do pacote. Depois, execute python3 -m myapp a partir da raiz do projeto. O Python executa o __main__.py do pacote para essa forma de invocação de -m, e esse arquivo pode usar importações relativas ao pacote. A documentação de __main__ do Python explica esse padrão de ponto de entrada de pacote.
Verifique estes detalhes se o erro persistir
- Confirme seu diretório de trabalho. Execute
pwdno Terminal e certifique-se de que ele exibe a raiz do projeto, o diretório que contémmyapp. Se o pacote estiver aninhado sobsrc, por exemplo, use o nome do pacote importável e garanta que o ambiente configurado do projeto o disponibilize; simplesmente mudar para a pasta mãe errada não resolverá a descoberta do módulo. - Use o nome real do pacote. Em
python3 -m myapp.cli,myappdeve ser o nome do diretório do pacote (ou seu nome de importação), eclio nome do arquivo do módulo sem.py. - Verifique a linha do traceback. Se a importação relativa for resolvida, mas uma importação posterior falhar, isso pode ser um módulo ou dependência ausente diferente. Corrigir o contexto de inicialização resolve apenas a falha de contexto de pacote.
- Evite o
PYTHONPATHcomo a primeira correção. Ele altera o caminho de busca de importação do Python, mas não transforma por si só um arquivo executado diretamente em um módulo de pacote nomeado adequadamente. O Python também alerta quePYTHONPATHpode afetar várias versões e ambientes do Python. A referência de inicialização de caminho do Python descreve seu escopo. - Se a execução direta do arquivo for um requisito, considere alterar o código para usar uma importação absoluta apropriada e garantir que o pacote esteja instalado ou de outra forma importável naquele ambiente. Essa é uma escolha de configuração diferente; não misture uma importação relativa ao pacote com a expectativa de que o arquivo se comporte como um script autônomo.
A regra prática é corresponder o comando ao estilo de importação: para uma importação relativa ao pacote, execute o módulo a partir do diretório pai do pacote com python -m. Para um comando de pacote, forneça __main__.py e execute o pacote com -m.
Fontes
- 5. O sistema de importação — Python Software Foundation
- A inicialização do caminho de busca de módulos sys.path — Python Software Foundation
- main — Ambiente de código de nível superior — Python Software Foundation
- Alterar o shell padrão no Terminal do Mac — Apple