Skip to content

9. Importações

O erro encontrado na versão 1 do exercício da aplicação leva-nos a explorar o papel da instrução [import] com maior profundidade.

Image

9.1. Scripts [import_01]

O script [imported] será importado por vários scripts (também chamados de módulos):

Image

1
2
3
4
5
#  imported module
#  this instruction will be executed each time the module is imported
print("2")
#  variable belonging to the imported module
x=4

Um módulo é executado quando é importado. Assim, quando o módulo [importado] é importado:

  • a saída da linha 3 ocorrerá;
  • à variável x na linha 5 será atribuído o seu valor;

O script [main_01] é o seguinte:

1
2
3
4
#  an imported module is executed
import imported
#  use of the x variable of the imported module
print(imported.x)
  • Linha 2: O módulo [importado] é importado. Isto fará com que seja executado:
    • o valor 2 será exibido;
    • a variável x é criada com o valor 4;
  • Linha 4: A variável x do módulo importado é utilizada;

No PyCharm, é reportado um erro:

Em [1], o PyCharm indica que não reconhece o módulo [importado]. Em termos técnicos, isto significa que a pasta que contém o módulo [importado] não se encontra no Python Path do PyCharm. O Python Path é o conjunto de pastas nas quais os módulos importados são procurados. Para resolver este problema, basta definir a pasta que contém o módulo [importado] como a pasta [Root Sources], neste caso a pasta [import/01]:

Image

Image

Após este passo, a pasta [import/01] é adicionada ao Python Path do PyCharm e o erro desaparece:

Image

  • em [1], a pasta [01] mudou de cor;
  • em [2-3], já não há erro;

Os resultados da execução são os seguintes:

1
2
3
4
5
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/01/main_01.py
2
4

Process finished with exit code 0

Comentários

  • A linha 2 é o resultado da execução do módulo importado;
  • A linha 3 exibe o valor da variável x do módulo importado;

A principal lição a reter deste exemplo é o conceito importante de que um módulo (ou script) importado é executado.

O script [main_02] é o seguinte:

1
2
3
4
#  import the x variable from the imported module
from imported import x
#  we display it
print(x)
  • A linha 2 utiliza uma sintaxe de importação diferente [from module import object1, object2, …]. Aqui, importamos a variável [imported.x]. Com esta sintaxe, a variável x torna-se uma variável do script [main_02]. Já não precisamos de a preceder com o seu módulo [imported];
  • Linha 4: Imprimimos a variável x de [main_02];

Os resultados da execução são os seguintes:

1
2
3
4
5
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/01/main_02.py
2
4

Process finished with exit code 0

O script [main_03] é o seguinte:

1
2
3
4
#  import everything visible in the imported module
from imported import *
#  use the x variable of the imported module
print(x)

A notação [import *] na linha 2 significa que importamos todos os objetos visíveis do módulo importado (variáveis, funções).

Os resultados são os seguintes:

1
2
3
4
5
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/01/main_03.py
2
4

Process finished with exit code 0

O script [main_04] é o seguinte:

1
2
3
4
5
#  import the x variable from the imported module
#  and rename it y
from imported import x as y
#  display variable y
print(y) 

A linha 3 mostra que podemos importar um objeto do módulo imported e atribuir-lhe um alias. Aqui, a variável [imported.x] torna-se a variável [main_04.y]. Os resultados são os mesmos de antes.

9.2. Script [import_02]

Image

O módulo importado [module1.py] é o seguinte:

1
2
3
#  a function
def f1():
    print("f1")

O módulo importado define uma função, o que é um cenário comum.

O script [main_01] é o seguinte:

1
2
3
4
#  import
import module1
#  execution f1
module1.f1()
  • Linha 2: o módulo é importado. Será executado. Aqui, não é exibido nada;
  • linha 4: a função [f1] do módulo importado é executada;

Os resultados da execução são os seguintes:

1
2
3
4
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/02/main_01.py
f1

Process finished with exit code 0

Nota: Para evitar que o PyCharm indique um erro na importação na linha 2, deve colocar a pasta que contém [module1] nos [Diretórios Raiz] do PyCharm:

Image

Em [1], a pasta [02] colocada no diretório [Root Sources] fica azul. Note que o erro relatado não impede que os scripts sejam executados corretamente aqui. Na verdade, quando o script [main_0x] é executado, a pasta do script é automaticamente adicionada ao Python Path. Como resultado, o [module1] é encontrado. A partir de agora, quando uma pasta aparecer a azul numa captura de ecrã, significa que foi colocada nos [Root Sources] do PyCharm.

O script [main_02] é o seguinte:

1
2
3
4
#  import
from module1 import f1
#  execution f1
f1()
  • A linha 2 importa a função [f1] do módulo [module1];
  • Na linha 4, a função f1 é utilizada;

Os resultados são idênticos aos do script [main_01].

9.3. Scripts [import_03]

Image

Nota: [03] encontra-se na pasta [Root Sources] do projeto.

Os novos scripts irão importar o módulo [module2], que não se encontra na mesma pasta que eles.

O script [module2] é o seguinte:

1
2
3
#  a function
def f2():
    print("f2")

O script define, portanto, uma função [f2].

O script [main_01] é o seguinte:

1
2
3
4
#  class2 module import
import dir1.module2
#  execution f2
dir1.module2.f2()
  • Linha 2: Utilizamos uma notação especial para indicar como localizar o módulo [module2]. [dir1.module2] deve ser interpretado como o caminho [dir1/module2]: para encontrar [module2], comece pela pasta do script atual [main_01], depois aceda a [dir1] e, aí, encontrará [module2]. Tenha em mente que o ponto de partida do caminho é a pasta do script que está a importar;
  • linha 4: para executar a função [f2] do [module2];

Os resultados são os seguintes:

1
2
3
4
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/03/main_01.py
f2

Process finished with exit code 0

Linha 2, o resultado da função [f2].

O script [main_02] é o seguinte:

1
2
3
4
#  import module dir1.module2 and rename it
import dir1.module2 as module2
#  execution f2
module2.f2()

Na linha 2, renomeamos o módulo [dir1.module] para simplificar o código na linha 4.

O script [main_03] é o seguinte:

1
2
3
4
#  import function f2 from module dir1.module2
from dir1.module2 import f2
#  execution f2
f2()

Desta vez, na linha 2, importamos apenas a função [f2], que passa a ser uma função do script [main_03] (linha 4).

Todos estes scripts funcionam tão bem no ambiente PyCharm como num console Python. A razão é que, em ambos os casos, o diretório do script executado — aqui, o diretório [03] — faz parte do Python Path. Como resultado, o diretório [dir1/module2] é encontrado.

9.4. Scripts [import_04]

Image

Aqui, as pastas [dir1] e [dir2] foram colocadas na [Root Sources] do projeto PyCharm.

O primeiro módulo importado é [module3]:

1
2
3
#  a function
def f3():
    print("f3")

O segundo módulo importado é [module4]:

1
2
3
4
5
6
from module3 import f3

#  a function
def f4():
    f3()
    print("f4")
  • Linha 1: Importamos a função [f3] do [module3]. Aqui, o [module3] está visível porque colocámos o seu diretório [dir1] nas [Root Sources];
  • Linhas 4–6: Definimos uma função [f4] que chama a função [f3] do [module3];

O script principal [main_01] é o seguinte:

1
2
3
4
#  import module4
from module4 import f4
#  execution f4
f4()
  • Linha 2: importar o módulo [module4]. Isto é visível porque colocámos o seu diretório [dir2] em [Root Sources] do PyCharm;
  • Linha 4: execução da função [f4] do [module4];

Os resultados da execução de [main_01] no PyCharm são os seguintes:

1
2
3
4
5
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/04/main_01.py
f3
f4

Process finished with exit code 0

Agora, vamos executar [main_01] num terminal Python (consola):

Image

Os resultados são os seguintes:

1
2
3
4
5
(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import\04>python main_01.py
Traceback (most recent call last):
  File "main_01.py", line 2, in <module>
    from module4 import f4
ModuleNotFoundError: No module named 'module4'

O que aconteceu? O terminal Python não tem conhecimento do Caminho Python do PyCharm ou das [Fontes Raiz]. Tem o seu próprio Caminho Python. Neste caminho, encontra-se sempre a pasta do script que está a ser executado, neste caso o script [main_01]. Por isso, conhece a pasta [import/04]. No script executado, encontra a linha:


from module4 import f4

O interpretador Python procura [module4] nas pastas do seu Python Path. No entanto, [module4] não se encontra em [import/04] — que está, de facto, no Python Path — mas em [import/04/dir2], que não está. Daí o erro.

Portanto, temos um problema que já enfrentámos anteriormente: um script que funciona corretamente no PyCharm pode falhar num terminal Python. Esta é uma questão recorrente que teremos de resolver.

9.5. Scripts [import_05]

Image

Nota: as pastas [dir1] e [dir2] são adicionadas ao Python Path. Note que já existe um conflito aqui: [module3] e [module4] serão encontrados em dois locais no Python Path do PyCharm:

  • em [import/04/dir1] e [import/05/dir1] para [module3];
  • em [import/04/dir2] e [import/05/dir2] para [module4];

Podemos então remover [import/04/dir1] e [import/04/dir2] das [Fontes Raiz] do projeto PyCharm. Acontece que, neste caso, [import/05/dir1] é uma cópia de [import/04/dir1] (o mesmo se aplica a [dir2]), pelo que não há qualquer problema. No entanto, vale a pena referir que, no próprio PyCharm, é necessário prestar atenção à lista de pastas em [Root Sources] para evitar conflitos.

O script [main_01] fica assim:

import sys
#  modify sys.path to include folders
#  containing the classes to be imported
sys.path.append(".")
sys.path.append("./dir1")
sys.path.append("./dir2")
#  import module4
from module4 import f4
#  execution f4
f4()

Estamos a tentar resolver o problema do caminho do Python. Queremos um que funcione tão bem no PyCharm como num terminal Python. Para tal, vamos configurá-lo nós próprios.

  • Linhas 4–6: Adicionamos os diretórios [., ./dir1, ./dir2] ao Python Path. Para que isto funcione, o diretório atual em tempo de execução deve ser o diretório [import/05]. Isto será verdade no PyCharm, mas não necessariamente num terminal Python, como veremos;
  • Linha 8: Importamos [module4]. Com base no que acabámos de fazer, este deve ser encontrado em [./dir2];

A execução no PyCharm produz os seguintes resultados:

1
2
3
4
5
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/05/main_01.py
f3
f4

Process finished with exit code 0

Agora, num terminal Python:


(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import\05>python main_01.py
f3
f4

Linha 1, o diretório de execução é [import/05].

Agora, vamos subir um nível na árvore de diretórios a partir de [import/05]:


(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import\05>cd ..
 
(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import>python 05/main_01.py
Traceback (most recent call last):
  File "05/main_01.py", line 8, in <module>
    from module4 import f4
ModuleNotFoundError: No module named 'module4'
  • Linha 2: Quando [main_01] é executado, já não estamos na pasta [import/05], mas sim em [import]. No entanto, escrevemos:

sys.path.append(".")
sys.path.append("./dir1")
sys.path.append("./dir2")

Isto adiciona as pastas [import, import/dir1, import/dir2] ao Python Path, o que não é de todo o que pretendemos. Note-se que adicionar pastas que não existem (import/dir1, import/dir2) ao Python Path não causa erros.

Fizemos progressos, mas não é suficiente. Precisamos de adicionar caminhos absolutos ao Python Path, e não relativos.

O script [main_02] é uma variante do [main_01] que utiliza um ficheiro de configuração [config.json]:


{
  "dependencies": [
    "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/05/dir1",
    "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/05/dir2"
  ]
}

O valor da chave [dependencies] é a lista de diretórios a adicionar ao Python Path. Note que aqui utilizámos caminhos absolutos em vez de caminhos relativos.

O script [main_02] utiliza o ficheiro [config.json] da seguinte forma:

import codecs
import json
import sys

#  configuration file
config_filename="C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/05/config.json"
#  reading the json configuration file
with codecs.open(config_filename, "r", "utf-8") as file:
    config = json.load(file)

#  modification of sys.path
for directory in config['dependencies']:
    sys.path.append(directory)

#  import module4
from module4 import f4
#  execution f4
f4()
  • linha 6: note que utilizámos o caminho absoluto para o ficheiro de configuração;
  • linhas 8–9: o ficheiro de configuração é lido. Um dicionário [config] (linha 9) é construído a partir do seu conteúdo;
  • linhas 11–13: os elementos da matriz [config['dependencies']] são adicionados ao Python Path. Note que, como utilizámos nomes de pastas absolutos em [config.json], estamos a adicionar nomes absolutos ao Python Path;
  • linha 16: [module4] é importado. Deve ser encontrado, uma vez que [dir2] está agora no Python Path;

A execução produz os mesmos resultados que para [main_02], exceto que o script continua a ser executado mesmo quando o diretório de execução já não é [import/05]:


(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import>python 05/main_02.py
f3
f4

Na linha 1, o diretório de execução é [import].

Fizemos progressos. Vimos:

  • que tivemos de construir o Python Path nós próprios;
  • que tivemos de incluir os caminhos absolutos de todas as pastas que contêm os módulos importados pela aplicação;

No entanto, colocar caminhos absolutos nos scripts não é uma solução. Assim que o projeto for transferido para outro local, deixa de funcionar. Temos de encontrar outra forma.

9.6. Scripts [import_06]

Image

Nota: as pastas [06, dir1, dir2] foram colocadas na pasta [Root Sources] do projeto PyCharm. As pastas [dir1, dir2] são idênticas às dos exemplos anteriores.

O ficheiro [config.json] é o seguinte:


{
  "rootDir": "C:/Data/st-2020/dev/python/cours-2020/v-02/imports/06",
  "relativeDependencies": [
    "dir1",
    "dir2"
  ],
  "absoluteDependencies": [
   ]
}

Estamos a introduzir dois tipos de caminhos:

  • caminhos absolutos, linhas 7–8;
  • caminhos relativos, linhas 3–6. Estes são relativos à raiz na linha 2. Assim, quando o projeto for movido para um novo local, apenas esta linha precisa de ser modificada;

O script [utils.py] utiliza o ficheiro [config.json] e constrói o Python Path:

#  imports
import codecs
import json
import os
import sys

#  application configuration
def config_app(config_filename: str) -> dict:
    #  config_filename: name of configuration file
    #  we let the exceptions rise

    #  using the configuration file
    with codecs.open(config_filename, "r", "utf-8") as file:
        config = json.load(file)
    #  add dependencies to sys.path
    rootDir = config['rootDir']
    #  add the project's relative dependencies to the syspath
    for directory in config['relativeDependencies']:
        #  add the dependency at the beginning of the syspath
        sys.path.insert(0, f"{rootDir}/{directory}")
    #  we add the project's absolute dependencies to the syspath
    for directory in config['absoluteDependencies']:
        #  add the dependency at the beginning of the syspath
        sys.path.insert(0, directory)
    #  return the configuration dictionary
    return config

#  executed script file
def get_scriptdir():
    return os.path.dirname(os.path.abspath(__file__))
  • linha 8: a função [config_app] recebe o nome do ficheiro de configuração como parâmetro;
  • linhas 12–14: o ficheiro de configuração é utilizado para criar o dicionário [config];
  • linha 20: [sys.path] é a lista de diretórios no Python Path;
  • linhas 17–20: as dependências relativas do ficheiro de configuração são adicionadas ao Python Path. São adicionadas ao início da lista [sys.path], linha 20. Isto porque, quando o Python procura um módulo, explora os diretórios em [sys.path] por ordem. No entanto, neste documento, módulos com os mesmos nomes estarão localizados em diretórios diferentes dentro do [sys.path]. Ao colocar as dependências da aplicação no início da matriz [sys.path], garantimos que estas sejam pesquisadas antes de outros diretórios no [sys.path] que possam conter módulos com os mesmos nomes;
  • linhas 21–24: as dependências absolutas do ficheiro de configuração são adicionadas ao Python Path;
  • linha 26: a configuração da aplicação é devolvida;
  • linhas 29–30: a função [get_scriptdir] devolve o caminho absoluto do diretório que contém o script atualmente em execução (aquele onde se encontra a chamada à função);

O script principal [main] é o seguinte:

#  imports
import sys

from utils import config_app


def affiche_path(msg: str):
    #  message
    print(f"{msg}------------------------------")
    #  sys.path
    for path in sys.path:
        print(path)


#  hand -------------
try:
    #  the sys.path is configured
    affiche_path("avant....")
    config = config_app(f"{get_scriptdir()}/config.json")
    affiche_path("après....")
    #  import module4
    from module4 import f4
    #  execution f4
    f4()
except BaseException as erreur:
    print(f"L'erreur suivante s'est produite : {erreur}")
finally:
    print("done")
  • linha 4: a função [config_app] é importada. Note que, uma vez que [utils] e [main] estão no mesmo diretório, esta [import] funciona sempre. Isto acontece porque o diretório do script principal é automaticamente adicionado ao Python Path;
  • linhas 7–12: a função [display_path] exibe a lista de pastas no Python Path;
  • linha 19: a aplicação é configurada. Note que o caminho absoluto do ficheiro de configuração é passado para a função [config_app]. Após esta instrução, o Python Path foi reconstruído;
  • linha 22: importamos [module4]. Graças à reconstrução do Python Path, este módulo será encontrado;
  • linha 24: a função [f4] é executada;

No ambiente PyCharm, os resultados da execução são os seguintes:


C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/06/main.py
avant....------------------------------
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import\06
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\fonctions\shared
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\impots\v01\shared

C:\Program Files\Python38\python38.zip
C:\Program Files\Python38\DLLs
C:\Program Files\Python38\lib
C:\Program Files\Python38
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\lib\site-packages
après....------------------------------
C:/Data/st-2020/dev/python/cours-2020/v-02/imports/06/dir2
C:/Data/st-2020/dev/python/cours-2020/v-02/imports/06/dir1
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import\06
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\fonctions\shared
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\impots\v01\shared
….
C:\Program Files\Python38\python38.zip
C:\Program Files\Python38\DLLs
C:\Program Files\Python38\lib
C:\Program Files\Python38
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\lib\site-packages
f3
f4
done
 
Process finished with exit code 0

Comentários

  • linhas 2–13: o caminho Python do PyCharm. Inclui todas as pastas localizadas na pasta [Root Sources] do projeto;
  • linhas 14–29: o caminho Python construído pela função [config_app]. As linhas 15–16 contêm as duas dependências que adicionámos;
  • linhas 22–27: os diretórios do sistema do interpretador Python que executou o script;
  • linhas 28–29: a execução prossegue normalmente;

Agora, voltemos ao contexto que anteriormente causou um erro de tempo de execução:

  • abra um terminal Python;
  • mude para um diretório diferente daquele que contém o script executado;

(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import>python 06/main.py
avant....------------------------------
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import\06
C:\Program Files\Python38\python38.zip
C:\Program Files\Python38\DLLs
C:\Program Files\Python38\lib
C:\Program Files\Python38
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\lib\site-packages
après....------------------------------
C:/Data/st-2020/dev/python/cours-2020/v-02/imports/06/dir2
C:/Data/st-2020/dev/python/cours-2020/v-02/imports/06/dir1
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import\06
C:\Program Files\Python38\python38.zip
C:\Program Files\Python38\DLLs
C:\Program Files\Python38\lib
C:\Program Files\Python38
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\lib\site-packages
f3
f4
done

Desta vez funciona (linhas 20–21). Note que [sys.path] não contém os mesmos diretórios que quando executado no PyCharm.

9.7. Scripts [import_07]

Melhoramos a solução anterior de duas maneiras:

  • substituímos o ficheiro de configuração [config.json] por um script [config.py]. De facto, o ficheiro JSON apresenta um problema significativo: não permite a inserção de comentários. O dicionário [config.json] pode ser substituído por um dicionário Python, que tem a vantagem de permitir a inserção de comentários;
  • utilizamos um módulo visível para todos os projetos Python na máquina;

9.7.1. Instalação de um módulo para toda a máquina

Image

Acima, criamos uma pasta [packages/myutils] no projeto PyCharm (os nomes não importam).

O script [myutils.py] é o seguinte:

#  imports
import sys
import os


def set_syspath(absolute_dependencies: list):
    #  absolute_dependencies: a list of absolute folder names

    #  add the project's absolute dependencies to the syspath
    for directory in absolute_dependencies:
        #  we check the existence of the file
        existe = os.path.exists(directory) and os.path.isdir(directory)
        if not existe:
            #  an exception is lifted
            raise BaseException(f"[set_syspath] le dossier du Python Path [{directory}] n'existe pas")
        else:
            #  add the folder at the beginning of the syspath
            sys.path.insert(0, directory)
  • linhas 6–18: a função [set_syspath] cria um Python Path com a lista de diretórios que lhe é passada como parâmetro;
  • linhas 12–15: verificamos se o diretório a ser adicionado ao Python Path existe;

O script [__init.py__] (com dois sublinhados antes e depois do nome; isto é obrigatório) é o seguinte:


from .myutils import set_syspath

Importamos a função [set_syspath] do script [myutils]. A notação [.myutils] refere-se ao caminho [./myutils], o que significa que o script [myutils] está localizado no mesmo diretório que [__init.py]. Poderíamos ter usado a notação [myutils]. No entanto, vamos criar um módulo [myutils] válido para toda a máquina. Como resultado, a notação [from myutils import set_syspath] tornar-se-ia ambígua. Referir-se-ia à importação do script [myutils] do diretório atual ou do script [myutils] válido para toda a máquina? A notação [.myutils] resolve essa ambiguidade.

O script [setup.py] (também aqui, o nome é fixo) é o seguinte:


from setuptools import setup
 
setup(name='myutils',
      version='0.1',
      description='Utilitaire fixant le Python Path',
      url='#',
      author='st',
      author_email='st@gmail.com',
      license='MIT',
      packages=['myutils'],
      zip_safe=False)

Neste script, descrevemos o módulo que vamos criar. Aqui, vamos criá-lo localmente. No entanto, o mesmo processo é utilizado para criar um módulo distribuído oficialmente (ver |pypi|). Os pontos importantes aqui são os seguintes:

  • linha 3: o nome do módulo que está a ser criado;
  • linha 4: a versão do módulo;
  • linha 5: a sua descrição;
  • linhas 7–8: o autor do módulo;

Para instalar este módulo com âmbito em toda a máquina, proceda da seguinte forma:

Image

Em seguida, no terminal Python, digite o seguinte comando [pip install .]:


(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\packages>pip install .
Processing c:\data\st-2020\dev\python\cours-2020\python3-flask-2020\packages
Using legacy setup.py install for myutils, since package 'wheel' is not installed.
Installing collected packages: myutils
  Attempting uninstall: myutils
    Found existing installation: myutils 0.1
    Uninstalling myutils-0.1:
      Successfully uninstalled myutils-0.1
    Running setup.py install for myutils ... done
Successfully installed myutils-0.1

A partir de agora, qualquer script na máquina pode importar o módulo [myutils] sem que este esteja no código do projeto.

9.7.2. O script [config.py]

Image

O script [config.py] gere a configuração da aplicação:

def configure():
    import os

    #  absolute name of the configuration script folder
    script_dir = os.path.dirname(os.path.abspath(__file__))
    #  absolute paths of folders to put in the syspath
    absolute_dependencies = [
        #  local files
        f"{script_dir}/dir1",
        f"{script_dir}/dir2",
    ]
    #  update syspath
    from myutils import set_syspath
    set_syspath(absolute_dependencies)

    #  return the config
    return {}
  • Linha 1: A função [configure] lida com a configuração da aplicação;
  • linhas 7–10: o dicionário que anteriormente se encontrava em [config.json];
  • linhas 9–10: uma vez que estamos num script, podemos aceder diretamente aos nomes absolutos das pastas [dir1, dir2];
  • linhas 12–14: usamos a função [set_syspath] do módulo [myutils] que acabámos de criar para definir o Python Path para a configuração;
  • linha 20: devolvemos o dicionário de configuração da aplicação. Aqui, está vazio;

9.7.3. O script [main.py]

O script principal [main] é o seguinte:

#  configure the application
import config

config = config.configure()

#  syspath is configured - imports can be made
from module4 import f4

#  hand -------------
try:
    f4()
except BaseException as erreur:
    print(f"L'erreur suivante s'est produite : {erreur}")
finally:
    print("done")
  • Linhas 2–4: Configuramos a aplicação utilizando o módulo [config.py]. Este está acessível porque se encontra no mesmo diretório que o script principal. No entanto, o diretório do script principal faz sempre parte do Python Path;
  • quando chegamos à linha 6, o Python Path já foi construído para incluir a pasta do módulo [module4]. Podemos, portanto, importá-lo na linha 7;
  • linhas 10–15: tudo o que resta é executar a função [f4];

Os resultados da execução no PyCharm são os seguintes:

1
2
3
4
5
6
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/07/main.py
f3
f4
done

Process finished with exit code 0

Num terminal Python fora do diretório do script principal, os resultados são os seguintes:

1
2
3
4
(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import>python 07/main.py
f3
f4
done

A partir de agora, seguiremos sempre o mesmo procedimento para configurar uma aplicação:

  • a presença de um script [config.py] no diretório do script principal. Este script contém uma função [configure] que tem duas finalidades:
    • construir o Python Path para a aplicação. Para tal, o [config.py] lista todas as pastas que contêm os módulos utilizados pela aplicação e constrói o Python Path utilizando os seus caminhos absolutos;
    • construir o dicionário [config] para a configuração da aplicação;

Aplicamos esta abordagem à segunda versão do exercício prático. Como se deve lembrar, a versão 1 funcionava no ambiente PyCharm, mas não num terminal Python. O problema decorria do caminho do Python.