Skip to content

9. Importazioni

L'errore riscontrato nella versione 1 dell'esercizio dell'applicazione ci porta ad approfondire il ruolo dell'istruzione [import].

Image

9.1. Script [import_01]

Lo script [imported] verrà importato da vari script (chiamati anche moduli):

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

Un modulo viene eseguito quando viene importato. Quindi, quando il modulo [importato] viene importato:

  • verrà visualizzato il risultato della riga 3;
  • alla variabile x nella riga 5 verrà assegnato il suo valore;

Lo script [main_01] è il seguente:

1
2
3
4
#  an imported module is executed
import imported
#  use of the x variable of the imported module
print(imported.x)
  • Riga 2: Il modulo [imported] viene importato. Ciò ne causerà l'esecuzione:
    • verrà visualizzato il valore 2;
    • viene creata la variabile x con il valore 4;
  • Riga 4: Viene utilizzata la variabile x del modulo importato;

In PyCharm viene segnalato un errore:

In [1], PyCharm indica che non riconosce il modulo [importato]. In termini tecnici, ciò significa che la cartella contenente il modulo [importato] non si trova nel Python Path di PyCharm. Il Python Path è l'insieme delle cartelle in cui vengono cercati i moduli importati. Per risolvere questo problema, è sufficiente impostare la cartella contenente il modulo [importato] come cartella [Root Sources], in questo caso la cartella [import/01]:

Image

Image

Dopo questo passaggio, la cartella [import/01] viene aggiunta al Python Path di PyCharm e l'errore scompare:

Image

  • in [1], la cartella [01] ha cambiato colore;
  • in [2-3], non c'è più alcun errore;

I risultati dell'esecuzione sono i seguenti:

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

Commenti

  • La riga 2 è il risultato dell'esecuzione del modulo importato;
  • La riga 3 visualizza il valore della variabile x dal modulo importato;

Il concetto chiave da trarre da questo esempio è l'importanza del fatto che un modulo (o script) importato venga eseguito.

Lo script [main_02] è il seguente:

1
2
3
4
#  import the x variable from the imported module
from imported import x
#  we display it
print(x)
  • La riga 2 utilizza una sintassi di importazione diversa [from module import object1, object2, …]. Qui importiamo la variabile [imported.x]. Con questa sintassi, la variabile x diventa una variabile dello script [main_02]. Non è più necessario anteporle il prefisso del modulo [imported];
  • Riga 4: stampiamo la variabile x da [main_02];

I risultati dell'esecuzione sono i seguenti:

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

Lo script [main_03] è il seguente:

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

La notazione [import *] alla riga 2 significa che importiamo tutti gli oggetti visibili dal modulo importato (variabili, funzioni).

I risultati sono i seguenti:

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

Lo script [main_04] è il seguente:

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) 

La riga 3 mostra che possiamo importare un oggetto dal modulo imported e assegnargli un alias. Qui, la variabile [imported.x] diventa la variabile [main_04.y]. I risultati sono gli stessi di prima.

9.2. Script [import_02]

Image

Il modulo importato [module1.py] è il seguente:

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

Il modulo importato definisce una funzione, il che è uno scenario comune.

Lo script [main_01] è il seguente:

1
2
3
4
#  import
import module1
#  execution f1
module1.f1()
  • Riga 2: il modulo viene importato. Verrà eseguito. Qui non viene visualizzato nulla;
  • riga 4: viene eseguita la funzione [f1] del modulo importato;

I risultati dell'esecuzione sono i seguenti:

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: per evitare che PyCharm segnali un errore durante l'importazione alla riga 2, è necessario inserire la cartella contenente [module1] nelle [Directory radice] di PyCharm:

Image

In [1], la cartella [02] inserita nella directory [Root Sources] diventa blu. Si noti che l'errore segnalato non impedisce agli script di funzionare correttamente in questo caso. Infatti, quando viene eseguito lo script [main_0x], la cartella dello script viene automaticamente aggiunta al Python Path. Di conseguenza, [module1] viene trovato. D'ora in poi, quando una cartella appare in blu in uno screenshot, significa che è stata inserita nelle [Root Sources] di PyCharm.

Lo script [main_02] è il seguente:

1
2
3
4
#  import
from module1 import f1
#  execution f1
f1()
  • La riga 2 importa la funzione [f1] dal modulo [module1];
  • Nella riga 4 viene utilizzata la funzione f1;

I risultati sono identici a quelli dello script [main_01].

9.3. Script [import_03]

Image

Nota: [03] si trova nella cartella [Root Sources] del progetto.

I nuovi script importeranno il modulo [module2], che non si trova nella stessa cartella in cui si trovano loro.

Lo script [module2] è il seguente:

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

Lo script definisce quindi una funzione [f2].

Lo script [main_01] è il seguente:

1
2
3
4
#  class2 module import
import dir1.module2
#  execution f2
dir1.module2.f2()
  • Riga 2: Utilizziamo una notazione speciale per indicare come individuare il modulo [module2]. [dir1.module2] va interpretato come il percorso [dir1/module2]: per trovare [module2], parti dalla cartella dello script corrente [main_01], poi entra in [dir1] e lì troverai [module2]. Tieni presente che il punto di partenza del percorso è la cartella dello script che sta importando;
  • riga 4: per eseguire la funzione [f2] da [module2];

I risultati sono i seguenti:

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

Riga 2, il risultato della funzione [f2].

Lo script [main_02] è il seguente:

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

Nella riga 2, rinominiamo il modulo [dir1.module] per semplificare il codice nella riga 4.

Lo script [main_03] è il seguente:

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

Questa volta, alla riga 2, importiamo solo la funzione [f2], che diventa quindi una funzione dello script [main_03] (riga 4).

Tutti questi script funzionano altrettanto bene nell'ambiente PyCharm quanto in una console Python. Il motivo è che in entrambi i casi la directory dello script eseguito — in questo caso, la directory [03] — fa parte del Python Path. Di conseguenza, viene individuata la directory [dir1/module2].

9.4. Script [import_04]

Image

Qui, le cartelle [dir1] e [dir2] sono state collocate nella [Root Sources] del progetto PyCharm.

Il primo modulo importato è [module3]:

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

Il secondo modulo importato è [module4]:

1
2
3
4
5
6
from module3 import f3

#  a function
def f4():
    f3()
    print("f4")
  • Riga 1: Importiamo la funzione [f3] dal [module3]. In questo caso, [module3] è visibile perché abbiamo inserito la sua directory [dir1] nella cartella [Root Sources];
  • Righe 4–6: Definiamo una funzione [f4] che chiama la funzione [f3] da [module3];

Lo script principale [main_01] è il seguente:

1
2
3
4
#  import module4
from module4 import f4
#  execution f4
f4()
  • Riga 2: importazione del modulo [module4]. Questo è visibile perché abbiamo inserito la sua directory [dir2] nelle [Root Sources] di PyCharm;
  • Riga 4: esecuzione della funzione [f4] da [module4];

I risultati dell'esecuzione di [main_01] in PyCharm sono i seguenti:

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

Ora, eseguiamo [main_01] in un terminale Python (console):

Image

I risultati sono i seguenti:

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'

Cosa è successo? Il terminale Python non conosce il Python Path di PyCharm né le [Root Sources]. Ha un proprio Python Path. In questo percorso è sempre presente la cartella dello script in esecuzione, in questo caso lo script [main_01]. Conosce quindi la cartella [import/04]. Nello script in esecuzione trova la riga:


from module4 import f4

L'interprete Python cerca [module4] nelle cartelle del proprio Python Path. Tuttavia, [module4] non si trova in [import/04] — che è effettivamente nel Python Path — ma in [import/04/dir2], che non lo è. Da qui l'errore.

Quindi ci troviamo di fronte a un problema già incontrato in precedenza: uno script che funziona correttamente in PyCharm potrebbe bloccarsi in un terminale Python. Si tratta di un problema ricorrente che dovremo risolvere.

9.5. Script [import_05]

Image

Nota: le cartelle [dir1] e [dir2] vengono aggiunte al Python Path. Si noti che qui c'è già un conflitto: [module3] e [module4] si troveranno in due posizioni nel Python Path di PyCharm:

  • in [import/04/dir1] e [import/05/dir1] per [module3];
  • nelle directory [import/04/dir2] e [import/05/dir2] per il [module4];

Possiamo quindi rimuovere [import/04/dir1] e [import/04/dir2] dalle [Root Sources] del progetto PyCharm. Si scopre che qui [import/05/dir1] è una copia di [import/04/dir1] (lo stesso vale per [dir2]), quindi non ci sono problemi. Tuttavia, vale la pena notare che all'interno di PyCharm stesso, è necessario prestare attenzione all'elenco delle cartelle in [Root Sources] per evitare conflitti.

Lo script [main_01] diventa il seguente:

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()

Stiamo cercando di risolvere il problema del percorso Python. Vogliamo che funzioni altrettanto bene in PyCharm quanto in un terminale Python. Per farlo, lo configureremo noi stessi.

  • Righe 4–6: aggiungiamo le directory [., ./dir1, ./dir2] al Python Path. Affinché ciò funzioni, la directory corrente in fase di esecuzione deve essere la directory [import/05]. Questo sarà vero in PyCharm ma non necessariamente in un terminale Python, come vedremo;
  • Riga 8: importiamo [module4]. In base a quanto appena fatto, dovrebbe trovarsi in [./dir2];

L'esecuzione in PyCharm produce i seguenti risultati:

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

Ora, in un terminale Python:


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

Riga 1, la directory di esecuzione è [import/05].

Ora saliamo di un livello nell'albero delle directory da [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'
  • Riga 2: Quando viene eseguito [main_01], non ci troviamo più nella cartella [import/05] ma in [import]. Tuttavia, abbiamo scritto:

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

Questo aggiunge le cartelle [import, import/dir1, import/dir2] al Python Path, il che non è affatto ciò che vogliamo. Si noti che l'aggiunta di cartelle inesistenti (import/dir1, import/dir2) al Python Path non causa errori.

Abbiamo fatto progressi, ma non è abbastanza. Dobbiamo aggiungere percorsi assoluti al Python Path, non relativi.

Lo script [main_02] è una variante di [main_01] che utilizza un file di configurazione [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"
  ]
}

Il valore della chiave [dependencies] è l'elenco delle directory da aggiungere al Python Path. Si noti che in questo caso abbiamo utilizzato percorsi assoluti anziché percorsi relativi.

Lo script [main_02] utilizza il file [config.json] come segue:

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()
  • riga 6: si noti che abbiamo utilizzato il percorso assoluto del file di configurazione;
  • righe 8–9: il file di configurazione viene letto. Dal suo contenuto viene costruito un dizionario [config] (riga 9);
  • righe 11-13: gli elementi dell'array [config['dependencies']] vengono aggiunti al Python Path. Si noti che, poiché in [config.json] sono stati utilizzati nomi di cartelle assoluti, al Python Path vengono aggiunti nomi assoluti;
  • riga 16: [module4] viene importato. Dovrebbe essere trovato poiché [dir2] è ora nel Python Path;

L'esecuzione produce gli stessi risultati di [main_02], tranne per il fatto che lo script continua a funzionare anche quando la directory di esecuzione non è più [import/05]:


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

Riga 1, la directory di esecuzione è [import].

Abbiamo fatto progressi. Abbiamo visto:

  • che dovevamo creare il Python Path da soli;
  • che dovevamo includere i percorsi assoluti di tutte le cartelle contenenti i moduli importati dall'applicazione;

Tuttavia, inserire i percorsi assoluti negli script non è una soluzione. Non appena il progetto viene spostato su un altro sito, smette di funzionare. Dobbiamo trovare un altro modo.

9.6. Script [import_06]

Image

Nota: le cartelle [06, dir1, dir2] sono state inserite nella directory [Root Sources] del progetto PyCharm. Le cartelle [dir1, dir2] sono identiche a quelle degli esempi precedenti.

Il file [config.json] è il seguente:


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

Introduciamo due tipi di percorsi:

  • percorsi assoluti, righe 7–8;
  • percorsi relativi, righe 3–6. Questi sono relativi alla radice alla riga 2. Pertanto, quando il progetto viene spostato in una nuova posizione, è necessario modificare solo questa riga;

Lo script [utils.py] utilizza il file [config.json] e costruisce il percorso Python:

#  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__))
  • riga 8: la funzione [config_app] riceve il nome del file di configurazione come parametro;
  • righe 12–14: il file di configurazione viene utilizzato per creare il dizionario [config];
  • riga 20: [sys.path] è l'elenco delle directory nel Python Path;
  • righe 17–20: le dipendenze relative dal file di configurazione vengono aggiunte al Python Path. Vengono aggiunte all’inizio dell’elenco [sys.path], riga 20. Questo perché quando Python cerca un modulo, esplora le directory in [sys.path] in ordine. Tuttavia, in questo documento, i moduli con lo stesso nome si troveranno in directory diverse all'interno di [sys.path]. Posizionando le dipendenze dell'applicazione all'inizio dell'array [sys.path], ci assicuriamo che queste vengano cercate prima delle altre directory in [sys.path] che potrebbero contenere moduli con lo stesso nome;
  • righe 21–24: le dipendenze assolute del file di configurazione vengono aggiunte al Python Path;
  • riga 26: viene restituita la configurazione dell'applicazione;
  • righe 29–30: la funzione [get_scriptdir] restituisce il percorso assoluto della directory contenente lo script attualmente in esecuzione (quella in cui si trova la chiamata alla funzione);

Lo script principale [main] è il seguente:

#  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")
  • riga 4: viene importata la funzione [config_app]. Si noti che, poiché [utils] e [main] si trovano nella stessa directory, questa [import] funziona sempre. Questo perché la directory dello script principale viene automaticamente aggiunta al Python Path;
  • righe 7–12: la funzione [display_path] visualizza l'elenco delle cartelle nel Python Path;
  • riga 19: l'applicazione viene configurata. Si noti che il percorso assoluto del file di configurazione viene passato alla funzione [config_app]. Dopo questa istruzione, il Python Path è stato ricostruito;
  • riga 22: importiamo [module4]. Grazie alla ricostruzione del Python Path, questo modulo verrà trovato;
  • riga 24: viene eseguita la funzione [f4];

Nell'ambiente PyCharm, i risultati dell'esecuzione sono i seguenti:


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

Commenti

  • righe 2–13: il percorso Python di PyCharm. Include tutte le cartelle presenti nella cartella [Root Sources] del progetto;
  • righe 14–29: il percorso Python costruito dalla funzione [config_app]. Le righe 15–16 contengono le due dipendenze che abbiamo aggiunto;
  • righe 22–27: le directory di sistema dell'interprete Python che ha eseguito lo script;
  • righe 28–29: l'esecuzione procede normalmente;

Ora torniamo al contesto che in precedenza ha causato un errore di runtime:

  • aprire un terminale Python;
  • passare a una directory diversa da quella contenente lo script eseguito;

(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

Questa volta funziona (righe 20–21). Si noti che [sys.path] non contiene le stesse directory rispetto a quando si esegue il codice in PyCharm.

9.7. Script [import_07]

Miglioriamo la soluzione precedente in due modi:

  • sostituiamo il file di configurazione [config.json] con uno script [config.py]. Infatti, il file JSON pone un problema significativo: non è possibile inserirvi commenti. Il dizionario [config.json] può essere sostituito da un dizionario Python, che ha il vantaggio di poter essere commentato;
  • utilizziamo un modulo visibile a tutti i progetti Python presenti sul computer;

9.7.1. Installazione di un modulo a livello di macchina

Image

Sopra, creiamo una cartella [packages/myutils] nel progetto PyCharm (i nomi non contano).

Lo script [myutils.py] è il seguente:

#  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)
  • righe 6–18: la funzione [set_syspath] crea un Python Path con l'elenco delle directory passate come parametro;
  • righe 12–15: verifichiamo che la directory da aggiungere al Python Path esista;

Lo script [__init.py__] (due trattini bassi prima e dopo il nome; è obbligatorio) è il seguente:


from .myutils import set_syspath

Importiamo la funzione [set_syspath] dallo script [myutils]. La notazione [.myutils] si riferisce al percorso [./myutils], il che significa che lo script [myutils] si trova nella stessa directory di [__init.py]. Avremmo potuto usare la notazione [myutils]. Tuttavia, creeremo un modulo [myutils] a livello di macchina. Di conseguenza, la notazione [from myutils import set_syspath] diventerebbe ambigua. Si riferisce all'importazione dello script [myutils] dalla directory corrente o allo script [myutils] a livello di macchina? La notazione [.myutils] risolve questa ambiguità.

Lo script [setup.py] (anche in questo caso, il nome è fisso) è il seguente:


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)

In questo script descriviamo il modulo che stiamo per creare. In questo caso, lo creeremo localmente. Tuttavia, lo stesso processo viene utilizzato per creare un modulo distribuito ufficialmente (vedi |pypi|). I punti importanti da tenere a mente sono i seguenti:

  • riga 3: il nome del modulo che si sta creando;
  • riga 4: la versione del modulo;
  • riga 5: la sua descrizione;
  • righe 7–8: l'autore del modulo;

Per installare questo modulo a livello di macchina, procedere come segue:

Image

Quindi, nel terminale Python, digitare il seguente 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

D'ora in poi, qualsiasi script sul computer potrà importare il modulo [myutils] senza che questo sia presente nel codice del progetto.

9.7.2. Lo script [config.py]

Image

Lo script [config.py] gestisce la configurazione dell'applicazione:

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 {}
  • Riga 1: la funzione [configure] gestisce la configurazione dell'applicazione;
  • righe 7–10: il dizionario che prima si trovava in [config.json];
  • righe 9-10: dato che siamo in uno script, possiamo accedere direttamente ai nomi assoluti delle cartelle [dir1, dir2];
  • righe 12–14: utilizziamo la funzione [set_syspath] del modulo [myutils] appena creato per impostare il Python Path per la configurazione;
  • riga 20: restituiamo il dizionario di configurazione dell'applicazione. Qui è vuoto;

9.7.3. Lo script [main.py]

Lo script principale [main] è il seguente:

#  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")
  • Righe 2–4: Configuriamo l'applicazione utilizzando il modulo [config.py]. Questo è accessibile perché si trova nella stessa directory dello script principale. Tuttavia, la directory dello script principale fa sempre parte del Python Path;
  • quando arriviamo alla riga 6, il Python Path è stato costruito in modo da includere la cartella del modulo [module4]. Possiamo quindi importarlo alla riga 7;
  • righe 10–15: non resta che eseguire la funzione [f4];

I risultati dell'esecuzione in PyCharm sono i seguenti:

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

In un terminale Python al di fuori della directory dello script principale, i risultati sono i seguenti:

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

D'ora in poi, seguiremo sempre la stessa procedura per configurare un'applicazione:

  • la presenza di uno script [config.py] nella directory dello script principale. Questo script contiene una funzione [configure] che ha due scopi:
    • costruire il Python Path per l'applicazione. Per farlo, [config.py] elenca tutte le cartelle contenenti i moduli utilizzati dall'applicazione e costruisce il Python Path utilizzando i loro percorsi assoluti;
    • creare il dizionario [config] per la configurazione dell'applicazione;

Applichiamo questo approccio alla seconda versione dell'esercizio pratico. Come forse ricorderete, la versione 1 funzionava nell'ambiente PyCharm ma non in un terminale Python. Il problema era dovuto al percorso Python.