9. Importy
Błąd napotkany w wersji 1 ćwiczenia praktycznego skłania nas do dokładniejszego przyjrzenia się roli instrukcji [import].

9.1. Skrypty [import_01]
Skrypt [imported] będzie importowany przez różne skrypty (zwane również modułami):

# moduł zaimportowany
# ta instrukcja zostanie wykonana za każdym razem, gdy moduł zostanie zaimportowany
print("2")
# zmienna należąca do zaimportowanego modułu
x=4
Moduł jest uruchamiany w momencie jego zaimportowania. Tak więc po zaimportowaniu modułu [imported]:
- zostanie wyświetlony wiersz 3;
- zmienna x w wierszu 5 otrzyma swoją wartość;
Skrypt [main_01] wygląda następująco:
# importowany moduł jest wykonywany
import imported
# użycie zmiennej x z importowanego modułu
print(imported.x)
- wiersz 2: importowany jest moduł [imported]. Spowoduje to jego wykonanie:
- zostanie wyświetlona wartość 2;
- tworzona jest zmienna x o wartości 4;
- wiersz 4: wykorzystuje się zmienną x z zaimportowanego modułu;
W module PyCharm zgłaszany jest błąd:
W module [1] moduł PyCharm zgłasza, że nie rozpoznaje modułu [imported]. Z technicznego punktu widzenia oznacza to, że folder zawierający moduł [imported] nie znajduje się w ścieżce Python Path modułu PyCharm. Ścieżka Python Path to zbiór folderów, w których szukane są importowane moduły. Aby rozwiązać ten problem, wystarczy zadeklarować w module [Sources root] folder, w którym znajduje się moduł [imported], w tym przypadku folder [import/01]:


Po wykonaniu tej operacji folder [import/01] zostaje dodany do ścieżki Python Path w Pycharmie, a błąd znika:

- w pliku [1] folder [01] zmienił kolor;
- w [2-3] nie ma już błędu;
Wyniki wykonania są następujące:
Komentarze
- wiersz 2 zawiera wynik wykonania zaimportowanego modułu;
- w wierszu 3 wyświetlana jest wartość zmiennej x z zaimportowanego modułu;
Z tego przykładu należy zapamiętać ważną zasadę, że zaimportowany moduł (lub skrypt) jest wykonywany.
Skrypt [main_02] wygląda następująco:
# importujemy zmienną x z importowanego modułu
from imported import x
# wyświetla się
print(x)
- W wierszu 2 mamy inną składnię importu: [from module import objet1, objet2, …]. Tutaj importujemy zmienną [imported.x]. Dzięki tej składni zmienna x staje się zmienną skryptu [main_02]. Nie ma już potrzeby poprzedzania jej prefiksem modułu [imported];
- wiersz 4: wyświetlana jest zmienna x ze skryptu [main_02];
Wyniki wykonania są następujące:
Skrypt [main_03] wygląda następująco:
# importuje się wszystko, co jest widoczne z importowanego modułu
from imported import *
# używa się zmiennej x z importowanego modułu
print(x)
Zapis [import *] w wierszu 2 oznacza, że importowane są wszystkie widoczne obiekty z importowanego modułu (zmienne, funkcje).
Wyniki są następujące:
Skrypt [main_04] wygląda następująco:
# importujemy zmienną x z importowanego modułu
# i zmieniamy jej nazwę na y
from imported import x as y
# wyświetla się zmienną y
print(y)
Wiersz 3 pokazuje, że można zaimportować obiekt z importowanego modułu i nadać mu alias. W tym przypadku zmienna [imported.x] staje się zmienną [main_04.y]. Wyniki są takie same jak poprzednio.
9.2. Skrypt [import_02]

Zaimportowany moduł [module1.py] wygląda następująco:
# funkcja
def f1():
print("f1")
Zaimportowany moduł definiuje funkcję, co jest częstym przypadkiem.
Skrypt [main_01] ma następującą postać:
# import
import module1
# wykonanie funkcji f1
module1.f1()
- w wierszu 2 moduł jest importowany. Zostanie on wykonany. W tym przypadku nie wyświetla on niczego;
- wiersz 4: wykonywana jest funkcja [f1] z zaimportowanego modułu;
Wyniki wykonania są następujące:
Uwaga: Aby funkcja PyCharm nie zgłaszała błędu podczas importowania wiersza 2, należy umieścić folder zawierający plik [module1] w folderze [Sources Root], który znajduje się w folderze PyCharm:

W pliku [1] folder [02] umieszczony w folderze [Sources Root] zmienił kolor na niebieski. Należy zauważyć, że zgłoszony błąd nie uniemożliwia w tym przypadku poprawnego wykonania skryptów. Podczas wykonywania skryptu [main_0x] katalog skryptu jest bowiem automatycznie dodawany do ścieżki Python Path. Dzięki temu skrypt [module1] zostaje znaleziony. Od tej pory, gdy na zrzucie ekranu folder jest zaznaczony na niebiesko, oznacza to, że został on umieszczony w ścieżce [Sources Root] przez skrypt PyCharm.
Skrypt [main_02] wygląda następująco:
# import
from module1 import f1
# wykonanie funkcji f1
f1()
- w wierszu 2 importowana jest funkcja [f1] z modułu [module1];
- w wierszu 4 używana jest funkcja f1;
Wyniki są identyczne jak w przypadku skryptu [main_01].
9.3. Skrypty [import_03]

Uwaga: [03] znajduje się w folderze [Sources Root] projektu.
Nowe skrypty zaimportują moduł [module2], który nie znajduje się w tym samym folderze co one.
Skrypt [module2] ma następującą treść:
# funkcja
def f2():
print("f2")
Skrypt definiuje zatem funkcję [f2].
Skrypt [main_01] ma następującą postać:
# import modułu Class2
import dir1.module2
# wykonanie f2
dir1.module2.f2()
- wiersz 2: stosuje się specjalny zapis, aby wskazać, jak znaleźć moduł [module2]. Należy odczytać [dir1.module2] jako ścieżkę [dir1/module2]: aby znaleźć [module2], należy zacząć od folderu bieżącego skryptu [main_01], następnie przejść do [dir1], a tam znaleźć [module2]. Należy pamiętać, że punktem wyjścia ścieżki jest folder skryptu, który dokonuje importu;
- wiersz 4: w celu uruchomienia funkcji [f2] z pliku [module2];
Wyniki są następujące:
Wiersz 2, wynik funkcji [f2].
Skrypt [main_02] wygląda następująco:
# import modułu dir1.module2, który zostanie przemianowany
import dir1.module2 as module2
# wykonanie funkcji f2
module2.f2()
W wierszu 2 zmieniono nazwę modułu na [dir1.module], aby uprościć zapis w wierszu 4.
Skrypt [main_03] ma następującą postać:
# import funkcji f2 z modułu dir1.module2
from dir1.module2 import f2
# wykonanie funkcji f2
f2()
Tym razem w wierszu 2 importujemy tylko funkcję [f2], która staje się wówczas funkcją skryptu [main_03] (wiersz 4).
Wszystkie te skrypty działają równie dobrze zarówno w kontekście PyCharm, jak i w konsoli języka Python. Powodem jest to, że w obu przypadkach katalog skryptu wykonywanego, w tym przypadku katalog [03], znajduje się w ścieżce Python Path. Dzięki temu katalog [dir1/module2] zostaje znaleziony.
9.4. Skrypty [import_04]

W tym przypadku foldery [dir1] i [dir2] zostały umieszczone w folderze [Sources Root] projektu PyCharm.
Pierwszym zaimportowanym modułem jest [module3]:
# funkcja
def f3():
print("f3")
Drugim zaimportowanym modułem jest [module4]:
from module3 import f3
# funkcja
def f4():
f3()
print("f4")
- W wierszu 1 importowana jest funkcja [f3] z pliku [module3]. W tym miejscu widoczna jest funkcja [module3], ponieważ umieszczono jej plik [dir1] w pliku [Sources Root];
- wiersze 4–6: definiujemy funkcję [f4], która wywołuje funkcję [f3] z pliku [module3];
Główny skrypt [main_01] wygląda następująco:
# importujemy moduł4
from module4 import f4
# wykonanie f4
f4()
- w wierszu 2 importowany jest moduł [module4]. Jest on widoczny, ponieważ jego folder [dir2] został umieszczony w folderze [Sources Root] modułu PyCharm;
- w wierszu 4: wykonanie funkcji [f4] z modułu [module4];
Wyniki wykonania funkcji [main_01] w funkcji PyCharm są następujące:
Teraz uruchommy [main_01] w terminalu (konsoli) języka Python:

Wyniki są następujące:
Co się stało? Terminal Pythona nie ma żadnej wiedzy na temat ścieżki Python Path ani skryptów [Sources Root] i PyCharm. Ma on własną ścieżkę Python Path. W tej ścieżce zawsze znajduje się katalog skryptu, który jest uruchamiany, w tym przypadku skryptu [main_01]. Zna więc katalog [import/04]. W uruchomionym skrypcie znajduje wiersz:
from module4 import f4
Interpreter języka Python szuka pliku [module4] w katalogach swojej ścieżki Python Path. Jednak plik [module4] nie znajduje się w pliku [import/04], który wprawdzie znajduje się w ścieżce Python Path, ale w pliku [import/04/dir2], którego tam nie ma. Stąd bierze się błąd.
Mamy więc do czynienia z problemem, z którym już się spotkaliśmy: skrypt działający poprawnie w PyCharm może ulec awarii w środowisku terminala Python. Jest to powtarzający się problem, który będziemy musieli rozwiązać.
9.5. Skrypty [import_05]

Uwaga: foldery [dir1] i [dir2] zostały umieszczone w ścieżce Python Path. Zauważmy już na wstępie, że występuje tu konflikt: pliki [module3] i [module4] będą znajdować się w dwóch miejscach ścieżki Python Path pliku PyCharm:
- w plikach [import/04/dir1] i [import/05/dir1] dla pliku [module3];
- w plikach [import/04/dir2] i [import/05/dir2] dla pliku [module4];
Można zatem wyodrębnić pliki [import/04/dir1] i [import/04/dir2] z pliku [Sources Root] w ramach projektu PyCharm. Okazuje się, że w tym przypadku [import/05/dir1] jest kopią [import/04/dir1] (to samo dotyczy [dir2]), więc nie ma tu żadnego problemu. Należy jednak zauważyć, że w samym pliku PyCharm należy zwrócić uwagę na listę folderów zawartych w pliku [Sources Root], aby uniknąć konfliktów.
Skrypt [main_01] przyjmuje następującą postać:
import sys
# modyfikujemy plik sys.path, aby uwzględnić w nim foldery
# zawierające klasy do zaimportowania
sys.path.append(".")
sys.path.append("./dir1")
sys.path.append("./dir2")
# importujemy moduł4
from module4 import f4
# wykonanie f4
f4()
Próbujemy rozwiązać problem ścieżki Python Path. Potrzebujemy takiej, która będzie działać zarówno w PyCharm, jak i w terminalu Python. W tym celu sami to naprawimy.
- Wiersze 4–6: dodajemy katalogi [., ./dir1, ./dir2] do ścieżki Python Path. Aby to zadziałało, katalog bieżący w momencie wykonywania musi być katalogiem [import/05]. Będzie to miało miejsce w katalogu PyCharm, ale niekoniecznie w terminalu Pythona, jak się przekonamy;
- wiersz 8: importujemy [module4]. Zgodnie z tym, co właśnie zrobiliśmy, powinien on zostać znaleziony w katalogu [./dir2];
Wykonanie w pliku PyCharm daje następujące wyniki:
Teraz w terminalu Python:
(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import\05>python main_01.py
f3
f4
Wiersz 1 – folder wykonania to [import/05].
Teraz cofnijmy się o jeden poziom w drzewie katalogów do [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'
- Wiersz 2: po uruchomieniu pliku [main_01] nie znajdujemy się już w folderze [import/05], lecz w folderze [import]. A przecież napisaliśmy:
sys.path.append(".")
sys.path.append("./dir1")
sys.path.append("./dir2")
To powoduje dodanie do ścieżki Python Path katalogów [import, import/dir1, import/dir2], co wcale nie jest naszym zamiarem. Należy zauważyć, że dodanie do ścieżki Python Path katalogów, które nie istnieją (import/dir1, import/dir2), nie powoduje błędów.
Zrobiliśmy postęp, ale to nie wystarczy. Do ścieżki Python Path należy dodawać nie ścieżki względne, lecz ścieżki bezwzględne.
Skrypt [main_02] jest odmianą skryptu [main_01], który wykorzystuje plik konfiguracyjny [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"
]
}
Wartością klucza [dependencies] jest lista folderów, które należy dodać do ścieżki Python Path. Należy zauważyć, że w tym przypadku podano nazwy bezwzględne, a nie względne.
Skrypt [main_02] wykorzystuje plik [config.json] w następujący sposób:
import codecs
import json
import sys
# plik konfiguracyjny
config_filename="C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/05/config.json"
# odczyt pliku konfiguracyjnego JSON
with codecs.open(config_filename, "r", "utf-8") as file:
config = json.load(file)
# modyfikacja pliku sys.path
for directory in config['dependencies']:
sys.path.append(directory)
# importujemy moduł4
from module4 import f4
# wykonanie f4
f4()
- wiersz 6: należy zwrócić uwagę, że użyto bezwzględnej nazwy pliku konfiguracyjnego;
- wiersze 8–9: plik konfiguracyjny jest odczytywany. Na podstawie jego zawartości tworzony jest słownik [config] (wiersz 9);
- wiersze 11–13: elementy tablicy [config['dependencies']] są dodawane do ścieżki Python Path. Należy zauważyć, że ponieważ w pliku [config.json] podano bezwzględne nazwy folderów, do ścieżki Python Path dodawane są bezwzględne nazwy;
- wiersz 16: importowany jest plik [module4]. Powinien on zostać znaleziony, ponieważ plik [dir2] znajduje się teraz w ścieżce Python Path;
Wykonanie daje takie same wyniki jak w przypadku pliku [main_02], z tą różnicą, że skrypt nadal działa, nawet gdy katalogem wykonawczym nie jest już [import/05]:
(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import>python 05/main_02.py
f3
f4
Wiersz 1: katalogem uruchomieniowym jest [import].
Zrobiliśmy postęp. Zauważyliśmy,
- że musieliśmy samodzielnie zbudować ścieżkę Python Path;
- że trzeba w nim umieścić bezwzględne ścieżki do wszystkich folderów zawierających moduły importowane przez aplikację;
Jednak umieszczanie nazw bezwzględnych w skryptach nie jest dobrym rozwiązaniem. Gdy tylko projekt zostanie przeniesiony na inną lokalizację, przestaje działać. Musimy znaleźć inne rozwiązanie.
9.6. Skrypty [import_06]

Uwaga: foldery [06, dir1, dir2] zostały umieszczone w folderach [Sources Root] projektu PyCharm. Foldery [dir1, dir2] są identyczne jak te z poprzednich przykładów.
Plik [config.json] ma następującą treść:
{
"rootDir": "C:/Data/st-2020/dev/python/cours-2020/v-02/imports/06",
"relativeDependencies": [
"dir1",
"dir2"
],
"absoluteDependencies": [
]
}
Wprowadzamy dwa rodzaje ścieżek:
- ścieżki bezwzględne, wiersze 7–8;
- ścieżki względne, wiersze 3–6. Są one względne względem katalogu głównego z wiersza 2. Dzięki temu, gdy projekt zostanie przeniesiony w inne miejsce, wystarczy zmodyfikować tylko ten wiersz;
Skrypt [utils.py] wykorzystuje plik [config.json] i tworzy ścieżkę Python Path:
# importy
import codecs
import json
import os
import sys
# konfiguracja aplikacji
def config_app(config_filename: str) -> dict:
# config_filename: nazwa pliku konfiguracyjnego
# zezwala się na zgłaszanie wyjątków
# wykorzystanie pliku konfiguracyjnego
with codecs.open(config_filename, "r", "utf-8") as file:
config = json.load(file)
# dodawanie zależności do sys.path
rootDir = config['rootDir']
# dodajemy zależności związane z projektem do ścieżki systemowej
for directory in config['relativeDependencies']:
# dodaje się zależność na początku ścieżki syspath
sys.path.insert(0, f"{rootDir}/{directory}")
# dodaje się do niego bezwzględne zależności projektu w ścieżce systemowej
for directory in config['absoluteDependencies']:
# dodaje się zależność na początku ścieżki systemowej
sys.path.insert(0, directory)
# tworzy się słownik konfiguracji
return config
# katalogu skryptu wykonywanego
def get_scriptdir():
return os.path.dirname(os.path.abspath(__file__))
- wiersz 8: funkcja [config_app] otrzymuje jako parametr nazwę pliku konfiguracyjnego;
- wiersze 12–14: plik konfiguracyjny jest wykorzystywany do utworzenia słownika [config];
- wiersz 20: [sys.path] to lista katalogów w ścieżce Python Path;
- wiersze 17–20: względne zależności z pliku konfiguracyjnego są dodawane do ścieżki Python Path. Są one dodawane na początku tablicy [sys.path], wiersz 20. W rzeczywistości, gdy Python szuka modułu, przeszukuje katalogi z [sys.path] w podanej kolejności. Jednak w tym dokumencie moduły o tych samych nazwach będą znajdować się w różnych katalogach [sys.path]. Umieszczając zależności aplikacji na początku tablicy [sys.path], zapewniamy, że zostaną one przeszukane przed innymi folderami w [sys.path], które mogłyby zawierać moduły o tych samych nazwach;
- wiersze 21–24: bezwzględne zależności pliku konfiguracyjnego są dodawane do ścieżki Python Path;
- wiersz 26: zwracana jest konfiguracja aplikacji;
- wiersze 29–30: funkcja [get_scriptdir] zwraca bezwzględną nazwę katalogu skryptu, który jest aktualnie wykonywany (tego, w którym znajduje się wywołanie funkcji);
Główny skrypt [main] wygląda następująco:
# importy
import sys
from utils import config_app
def affiche_path(msg: str):
# komunikat
print(f"{msg}------------------------------")
# sys.path
for path in sys.path:
print(path)
# main -------------
try:
# sys.path jest skonfigurowany
affiche_path("avant....")
config = config_app(f"{get_scriptdir()}/config.json")
affiche_path("après....")
# importujemy moduł4
from module4 import f4
# wykonanie f4
f4()
except BaseException as erreur:
print(f"L'erreur suivante s'est produite : {erreur}")
finally:
print("done")
- wiersz 4: importowana jest funkcja [config_app]. Należy zauważyć, że ponieważ funkcje [utils] i [main] znajdują się w tym samym folderze, funkcja [import] działa zawsze. Dzieje się tak, ponieważ folder zawierający skrypt główny jest automatycznie dodawany do ścieżki Python Path;
- wiersze 7–12: funkcja [affiche_path] wyświetla listę folderów ścieżki Python Path;
- wiersz 19: konfigurujemy aplikację. Należy zauważyć, że do funkcji [config_app] przekazujemy bezwzględną nazwę pliku konfiguracyjnego. Po tej instrukcji ścieżka Python Path została odbudowana;
- wiersz 22: importujemy [module4]. Dzięki odbudowie ścieżki Python Path moduł ten zostanie znaleziony;
- wiersz 24: wywołujemy funkcję [f4];
W kontekście PyCharm wyniki wykonania są następujące:
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
Komentarze
- wiersze 2–13: ścieżka Python Path dla PyCharm. Znajdują się w niej wszystkie foldery umieszczone w [Sources Root] projektu;
- wiersze 14–29: ścieżka Python Path utworzona przez funkcję [config_app]. W wierszach 15–16 znajdują się dwie zależności, które dodaliśmy;
- wiersze 22–27: katalogi systemowe interpretera języka Python, który wykonał skrypt;
- wiersze 28–29: wykonanie przebiega normalnie;
Teraz przejdźmy do kontekstu, który do tej pory powodował błąd wykonania:
- otwieramy terminal Pythona;
- przechodzimy do katalogu innego niż ten, w którym znajduje się wykonywany skrypt;
(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
Tym razem wszystko działa poprawnie (wiersze 20–21). Należy zauważyć, że [sys.path] nie zawiera tych samych folderów, co w przypadku wykonywania skryptu pod PyCharm.
9.7. Skrypty [import_07]
Ulepszamy poprzednie rozwiązanie na dwa sposoby:
- zastępujemy plik konfiguracyjny [config.json] skryptem [config.py]. Plik jSON stwarza bowiem poważny problem: nie można go opatrzyć komentarzami. Słownik [config.json] można zastąpić słownikiem w języku Python, który ma tę zaletę, że można go opatrzyć komentarzami;
- używamy modułu widocznego dla wszystkich projektów w języku Python na tym komputerze;
9.7.1. Instalacja modułu o zasięgu obejmującym cały komputer

Powyżej tworzymy folder [packages/myutils] w projekcie PyCharm (nazwy nie mają znaczenia).
Skrypt [myutils.py] wygląda następująco:
# importy
import sys
import os
def set_syspath(absolute_dependencies: list):
# absolute_dependencies: lista bezwzględnych nazw folderów
# do ścieżki systemowej dodaje się bezwzględne zależności projektu
for directory in absolute_dependencies:
# sprawdzamy, czy katalog istnieje
existe = os.path.exists(directory) and os.path.isdir(directory)
if not existe:
# rzucany jest wyjątek
raise BaseException(f"[set_syspath] le dossier du Python Path [{directory}] n'existe pas")
else:
# dodaje się folder na początek ścieżki syspath
sys.path.insert(0, directory)
- wiersze 6–18: funkcja [set_syspath] tworzy ścieżkę Python Path zawierającą listę folderów przekazanych jej jako parametr;
- wiersze 12–15: sprawdzane jest, czy folder, który ma zostać dodany do ścieżki Python Path, istnieje;
Skrypt [__init.py__] (dwa podkreślenia przed i po nazwie; jest to wymóg) wygląda następująco:
from .myutils import set_syspath
Importujemy funkcję [set_syspath] ze skryptu [myutils]. Notacja [.myutils] oznacza ścieżkę [./myutils], a więc skrypt [myutils] znajdujący się w tym samym folderze co [__init.py]. Można by było użyć notacji [myutils]. Jednak zamierzamy utworzyć moduł [myutils] o zasięgu maszynowym. W związku z tym notacja [from myutils import set_syspath] stałaby się niejednoznaczna. Czy chodzi o zaimportowanie skryptu [myutils] z bieżącego folderu, czy też skryptu [myutils] o zasięgu maszynowym? Notacja [.myutils] eliminuje tę niejednoznaczność.
Skrypt [setup.py] (również w tym przypadku nazwa jest narzucona) ma następującą postać:
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)
W tym skrypcie opisuje się moduł, który ma zostać utworzony. W tym przypadku utworzymy go lokalnie. Jednak ten sam proces stosuje się do tworzenia modułu dystrybuowanego oficjalnie (por. |pypi|). Najważniejsze kwestie to:
- wiersz 3: nazwa utworzonego modułu;
- wiersz 4: wersja modułu;
- wiersz 5: jego opis;
- wiersze 7–8: autor modułu;
Aby zainstalować ten moduł z zakresem działania na poziomie maszyny, należy postępować w następujący sposób:

Następnie w terminalu Python wpisujemy następujące polecenie: [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
Od tej chwili każdy skrypt na maszynie może importować moduł [myutils], nawet jeśli nie znajduje się on w kodzie projektu.
9.7.2. Skrypt [config.py]

Skrypt [config.py] zapewnia konfigurację aplikacji:
def configure():
import os
# absolutna nazwa folderu skryptu konfiguracyjnego
script_dir = os.path.dirname(os.path.abspath(__file__))
# absolutne ścieżki do folderów, które mają zostać dodane do syspath
absolute_dependencies = [
# foldery lokalne
f"{script_dir}/dir1",
f"{script_dir}/dir2",
]
# aktualizacja ścieżki syspath
from myutils import set_syspath
set_syspath(absolute_dependencies)
# zwracamy konfigurację
return {}
- wiersz 1: funkcja [configure] zapewnia konfigurację aplikacji;
- wiersze 7–10: słownik, który wcześniej znajdował się w pliku [config.json];
- wiersze 9–10: ponieważ mamy do czynienia ze skryptem, możemy bezpośrednio uzyskać bezwzględne nazwy folderów [dir1, dir2];
- wiersze 12–14: wykorzystujemy funkcję [set_syspath] z modułu [myutils], który właśnie utworzyliśmy, aby zdefiniować ścieżkę Python Path w konfiguracji;
- wiersz 20: zwracamy słownik konfiguracji aplikacji. W tym przypadku jest on pusty;
9.7.3. Skrypt [main.py]
Główny skrypt [main] wygląda następująco:
# konfiguracja aplikacji
import config
config = config.configure()
# ścieżka systemowa jest skonfigurowana – można przeprowadzić importy
from module4 import f4
# main -------------
try:
f4()
except BaseException as erreur:
print(f"L'erreur suivante s'est produite : {erreur}")
finally:
print("done")
- wiersze 2–4: konfigurujemy aplikację za pomocą modułu [config.py]. Jest on dostępny, ponieważ znajduje się w tym samym folderze co skrypt główny. Folder skryptu głównego jest zawsze częścią ścieżki Python Path;
- gdy dochodzimy do wiersza 6, ścieżka Python Path została skonstruowana z uwzględnieniem folderu modułu [module4]. Można go zatem zaimportować w wierszu 7;
- wiersze 10–15: pozostaje już tylko wywołać funkcję [f4];
Wyniki wykonania w module PyCharm są następujące:
W terminalu Python, poza folderem głównego skryptu, wyniki są następujące:
Od tej pory będziemy zawsze postępować w ten sam sposób, aby skonfigurować aplikację:
- obecność skryptu [config.py] w folderze głównego skryptu. Skrypt ten zawiera funkcję [configure], która ma dwa cele:
- utworzenie ścieżki Python Path dla aplikacji. W tym celu funkcja [config.py] deklaruje wszystkie foldery zawierające moduły używane przez aplikację i tworzy ścieżkę Python Path z ich nazwami bezwzględnymi;
- utworzenie słownika [config] zawierającego konfigurację aplikacji;
Stosujemy ten schemat do drugiej wersji ćwiczenia praktycznego. Pamiętamy bowiem, że wersja 1 działała w środowisku PyCharm, ale nie w terminalu Python. Problem wynikał ze ścieżki Python Path.