Resolução de módulos
O algoritmo exato: as cinco formas de pedir um módulo, e a ordem em que a linguagem procura cada uma.
"Module not found" é uma das mensagens mais frustrantes que existem, e quase sempre porque o algoritmo de busca é folclore. Aqui ele é curto o bastante para caber numa página — e é uma implementação só, em resolucao.py, usada pelo interpretador e pelo analisador.
As cinco formas#
| O que você escreve | Resolve contra |
|---|---|
Arcane.Math | a biblioteca padrão |
Python.numpy | a ponte para o Python (não passa pela busca em disco) |
./util, ../lib/util | a pasta do arquivo que escreve o import |
sub.modulo | a pasta do arquivo, depois o diretório atual |
validador | forge_modules/, subindo até achar um forge.toml |
Relativo resolve a partir do arquivo, nunca do diretório de onde se rodou. É o que permite mover a pasta inteira sem quebrar nada, e o que torna a leitura do arquivo suficiente para saber o que ele importa.
A ordem, para um caminho relativo#
adopt ./util procura, nesta ordem:
./util (o caminho exato, se for um arquivo)
./util.df
./util/main.df
./util/src/main.dfA ordem importa porque um arquivo e uma pasta com o mesmo nome podem coexistir. Sem ela declarada, a escolha dependeria da ordem em que o sistema de arquivos devolve os nomes.
A ordem, para um nome pontilhado#
adopt sub.modulo troca o ponto por separador de pasta — e só aqui, depois de o caso relativo ter sido descartado:
<pasta do arquivo>/sub/modulo.df
<pasta do arquivo>/sub/modulo/main.df
<pasta do arquivo>/sub/modulo/src/main.df
<diretório atual>/sub/modulo.df
<diretório atual>/sub/modulo/main.df
<diretório atual>/sub/modulo/src/main.dfHífen num caminho relativo#
adopt ./minha-lib as L funciona, e não é óbvio que devesse: o lexer entrega o hífen como operador de subtração. O segmento de caminho cola -, . e dígitos ao nome exigindo adjacência de coluna — sem essa guarda, a - b viraria um arquivo chamado a-b.
Um pacote se importa pelo próprio nome#
O teste de uma biblioteca escreve adopt validador, e não adopt ../src/main, porque precisa exercitá-la pelo caminho que um usuário usaria. A busca sobe até o forge.toml mais próximo e olha o forge_modules/ dali.
meu-projeto/
forge.toml o que você pediu (versionado)
forge.lock o que foi instalado (versionado)
forge_modules/ os pacotes (NÃO versionado)
src/main.dfQuando não acha#
A mensagem nomeia o que foi procurado, e não só o que faltou — um "não encontrado" sem a lista de tentativas obriga a adivinhar qual das cinco formas a linguagem achou que você estava usando.
O dataforge deps mostra o grafo de imports do projeto e acusa ciclo, usando o mesmo resolucao.py. Ele já teve uma terceira cópia da regra — uma expressão regular que começava em [A-Za-z_], de modo que ./vizinho nunca casava — e dizia "0 arquivos com imports próprios" em todo projeto do repositório.