Skip to content

9. Імпорти

Помилка, що виникла у версії 1 практичного завдання, спонукає нас детальніше розглянути роль оператора [import].

Image

9.1. Скрипти [import_01]

Скрипт [imported] імпортуватиметься різними скриптами (які також називаються модулями):

Image


# модуль імпортовано
# ця інструкція виконуватиметься щоразу, коли модуль імпортується
print("2")
# змінна, що належить до імпортованого модуля
x=4

Модуль виконується під час його імпорту. Отже, коли буде імпортовано модуль [imported]:

  • відбудеться відображення рядка 3;
  • змінна x у рядку 5 отримає своє значення;

Скрипт [main_01] має такий вигляд:


# імпортований модуль виконується
import imported
# використання змінної x з імпортованого модуля
print(imported.x)
  • рядок 2: імпортується модуль [imported]. Це призведе до його виконання:
    • буде виведено значення 2;
    • створюється змінна x із значенням 4;
  • рядок 4: використовується змінна x з імпортованого модуля;

У PyCharm повідомляється про помилку:

У [1] повідомлення PyCharm вказує, що модуль [imported] невідомий. З технічної точки зору це означає, що папка, яка містить модуль [imported], не входить до Python Path модуля PyCharm. Python Path — це сукупність папок, у яких шукаються імпортовані модулі. Щоб вирішити цю проблему, достатньо вказати [Sources root] як папку, у якій знаходиться модуль [imported], у даному випадку папку [import/01]:

Image

Image

Після цієї операції папка [import/01] додається до Python Path у Pycharm, і помилка зникає:

Image

  • у [1] папка [01] змінила колір;
  • у [2-3] помилки більше немає;

Результати виконання такі:

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

Коментарі

  • рядок 2 — це результат виконання імпортованого модуля;
  • у рядку 3 відображається значення змінної x імпортованого модуля;

З цього прикладу слід винести важливе поняття: імпортований модуль (або скрипт) виконується.

Скрипт [main_02] має такий вигляд:


# імпортується змінна x з імпортованого модуля
from imported import x
# вона виводиться на екран
print(x)
  • у рядку 2 ми бачимо інший синтаксис імпорту [from module import objet1, objet2, …]. Тут імпортується змінна [imported.x]. За цим синтаксисом змінна x стає змінною скрипта [main_02]. Більше не потрібно додавати префікс її модуля [imported];
  • рядок 4: виводимо змінну x зі скрипта [main_02];

Результати виконання такі:

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

Скрипт [main_03] має такий вигляд:


# імпортується все, що є видимим з імпортованого модуля
from imported import *
# використовується змінна x з імпортованого модуля
print(x)

Запис [import *] у рядку 2 означає, що імпортуються всі видимі об’єкти з імпортованого модуля (змінні, функції).

Результати такі:

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

Скрипт [main_04] має такий вигляд:


# імпортуємо змінну x з імпортованого модуля
# і перейменовуємо її на y
from imported import x as y
# виводиться змінна y
print(y) 

У рядку 3 показано, що можна імпортувати об’єкт з імпортованого модуля та надати йому псевдонім. Тут змінна [imported.x] стає змінною [main_04.y]. Результати такі самі, як і раніше.

9.2. Скрипт [import_02]

Image

Імпортований модуль [module1.py] має такий вигляд:


# функція
def f1():
    print("f1")

Імпортований модуль визначає функцію, що є типовим випадком.

Скрипт [main_01] має такий вигляд:


# імпорт
import module1
# виконання f1
module1.f1()
  • у рядку 2 модуль імпортується. Він буде виконаний. У цьому випадку він нічого не виводить;
  • рядок 4: виконується функція [f1] з імпортованого модуля;

Результати виконання такі:

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

Примітка: Щоб уникнути помилки PyCharm під час імпортування рядка 2, потрібно помістити папку, що містить [module1], у [Sources Root] з PyCharm:

Image

У файлі [1] папка [02], розміщена в [Sources Root], стала синьою. Слід зауважити, що зазначена помилка не заважає правильному виконанню скриптів. Адже під час виконання скрипта [main_0x] папка скрипта автоматично додається до Python Path. Таким чином, скрипт [module1] знайдено. Відтепер, коли на знімку екрана папка виділена синім кольором, це означає, що її було додано до [Sources Root] з PyCharm.

Скрипт [main_02] має такий вигляд:


# імпорт
from module1 import f1
# виконання f1
f1()
  • у рядку 2 імпортується функція [f1] з модуля [module1];
  • у рядку 4 використовується функція f1;

Результати збігаються з результатами скрипта [main_01].

9.3. Скрипти [import_03]

Image

Примітка: [03] знаходиться у папці [Sources Root] цього проєкту.

Нові скрипти імпортують модуль [module2], який не знаходиться в тій самій папці, що й вони.

Скрипт [module2] має такий вигляд:


# функція
def f2():
    print("f2")

Отже, скрипт визначає функцію [f2].

Скрипт [main_01] має такий вигляд:


# імпорт модуля Class2
import dir1.module2
# виконання f2
dir1.module2.f2()
  • рядок 2: використовується спеціальна нотація, щоб вказати, як знайти модуль [module2]. [dir1.module2] слід читати як шлях [dir1/module2]: щоб знайти [module2], слід почати з папки поточного скрипта [main_01], потім перейти до [dir1], і там знайти [module2]. Тут не слід забувати, що початковою точкою шляху є папка скрипта, який виконує імпорт;
  • рядок 4: для виконання функції [f2] зі скрипта [module2];

Результати такі:

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

Рядок 2 — результат функції [f2].

Скрипт [main_02] має такий вигляд:


# імпорт модуля dir1.module2, який перейменовується
import dir1.module2 as module2
# виконання f2
module2.f2()

У рядку 2 модуль [dir1.module] перейменовано, щоб спростити написання рядка 4.

Скрипт [main_03] має такий вигляд:


# імпорт функції f2 з модуля dir1.module2
from dir1.module2 import f2
# виконання f2
f2()

Цього разу у рядку 2 імпортуємо лише функцію [f2], яка стає функцією скрипта [main_03] (рядок 4).

Усі ці скрипти працюють однаково добре як у контексті PyCharm, так і в консолі Python. Причина в тому, що в обох випадках папка, з якої виконується скрипт, у даному випадку папка [03], входить до Python Path. Отже, папка [dir1/module2] знайдена.

9.4. Скрипти [import_04]

Image

Тут папки [dir1] та [dir2] були розміщені в папці [Sources Root] проекту PyCharm.

Першим імпортованим модулем є [module3]:


# функція
def f3():
    print("f3")

Другий імпортований модуль — [module4]:


from module3 import f3

# функція
def f4():
    f3()
    print("f4")
  • у рядку 1 імпортується функція [f3] з [module3]. Тут [module3] видно, оскільки його папку [dir1] було розміщено в [Sources Root];
  • рядки 4–6: визначається функція [f4], яка викликає функцію [f3] з [module3];

Головний скрипт [main_01] має такий вигляд:


# імпортуємо module4
from module4 import f4
# виконання f4
f4()
  • у рядку 2 імпортується модуль [module4]. Він є видимим, оскільки його папку [dir2] було розміщено в [Sources Root] модуля PyCharm;
  • у рядку 4: виконання функції [f4] з [module4];

Результати виконання [main_01] у PyCharm такі:

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

Тепер запустимо [main_01] у терміналі (консолі) Python:

Image

Результати такі:

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'

Що сталося? Термінал Python не має жодного уявлення про Python Path та про [Sources Root] і PyCharm. Він має власний Python Path. У ньому завжди є папка скрипта, що виконується, у даному випадку скрипт [main_01]. Отже, він знає папку [import/04]. У виконуваному скрипті він знаходить рядок:


from module4 import f4

Інтерпретатор Python шукає [module4] у папках свого Python Path. Однак [module4] знаходиться не в [import/04] (який дійсно є в Python Path), а в [import/04/dir2], якого там немає. Звідси й виникає помилка.

Отже, ми маємо проблему, з якою вже стикалися раніше: скрипт, що коректно виконується в PyCharm, може давати збій у контексті терміналу Python. Це повторювана проблема, яку нам доведеться вирішити.

9.5. Скрипти [import_05]

Image

Примітка: папки [dir1] та [dir2] додано до Python Path. Відразу зауважимо, що тут існує конфлікт: файли [module3] та [module4] будуть знайдені у двох місцях Python Path файлу PyCharm:

  • у [import/04/dir1] та [import/05/dir1] для [module3];
  • у [import/04/dir2] та [import/05/dir2] для [module4];

Тоді можна вилучити [import/04/dir1] та [import/04/dir2] з [Sources Root] проекту PyCharm. Виявляється, що в даному випадку [import/05/dir1] є копією [import/04/dir1] (те саме стосується [dir2]), тому проблем не виникає. Проте слід зауважити, що всередині самого файлу PyCharm потрібно звертати увагу на список папок у файлі [Sources Root], щоб уникнути конфліктів.

Скрипт [main_01] виглядає наступним чином:


import sys
# змінюємо sys.path, щоб включити до нього папки
# , що містять класи, які потрібно імпортувати
sys.path.append(".")
sys.path.append("./dir1")
sys.path.append("./dir2")
# імпортуємо module4
from module4 import f4
# виконання f4
f4()

Ми намагаємося вирішити проблему з Python Path. Нам потрібен такий, який працюватиме однаково добре як у PyCharm, так і в терміналі Python. Для цього ми виправимо це самостійно.

  • рядки 4–6: додаємо папки [., ./dir1, ./dir2] до Python Path. Щоб це працювало, поточна папка під час виконання має бути папкою [import/05]. Це буде дотримано в PyCharm, але не обов’язково — у терміналі Python, як ми побачимо;
  • рядок 8: імпортуємо [module4]. З огляду на те, що ми щойно зробили, його слід знайти в [./dir2];

Виконання у файлі PyCharm дає такі результати:

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

Тепер у терміналі Python:


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

У рядку 1 файл виконання — [import/05].

Тепер піднімемося на один рівень вище в дереві [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'
  • рядок 2: під час виконання [main_01] ми вже не перебуваємо в папці [import/05], а в [import]. Однак ми написали:

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

Це додає до Python Path папки [import, import/dir1, import/dir2], що зовсім не те, чого ми хочемо. Зауважимо, що додавання до Python Path папок, яких не існує (import/dir1, import/dir2), не викликає помилок.

Ми просунулися вперед, але цього недостатньо. До Python Path потрібно додавати не відносні, а абсолютні шляхи.

Скрипт [main_02] є варіантом [main_01], який використовує файл конфігурації [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"
  ]
}

Значенням ключа [dependencies] є список папок, які потрібно додати до Python Path. Зверніть увагу, що тут вказано абсолютні, а не відносні імена.

Скрипт [main_02] використовує файл [config.json] наступним чином:


import codecs
import json
import sys

# файл конфігурації
config_filename="C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/05/config.json"
# читання файлу конфігурації json
with codecs.open(config_filename, "r", "utf-8") as file:
    config = json.load(file)

# зміна sys.path
for directory in config['dependencies']:
    sys.path.append(directory)

# імпортуємо module4
from module4 import f4
# виконання f4
f4()

  • рядок 6: зверніть увагу, що використано абсолютне ім’я конфігураційного файлу;
  • рядки 8–9: файл конфігурації зчитується. На основі його вмісту створюється словник [config] (рядок 9);
  • рядки 11–13: елементи масиву [config['dependencies']] додаються до Python Path. Зауважимо, що оскільки в [config.json] вказано абсолютні імена папок, до Python Path додаються саме абсолютні імена;
  • рядок 16: імпортується файл [module4]. Його має бути знайдено, оскільки тепер файл [dir2] знаходиться в Python Path;

Виконання дає ті самі результати, що й для [main_02], за винятком того, що скрипт продовжує працювати, коли папкою виконання вже не є [import/05]:


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

У рядку 1 каталог виконання — [import].

Ми просунулися вперед. Ми побачили:

  • що нам потрібно самостійно побудувати Python Path;
  • що туди потрібно вносити абсолютні імена всіх папок, що містять модулі, які імпортує додаток;

Проте вказувати абсолютні шляхи у скриптах — це не вихід. Як тільки проект переносять на інший сервер, він перестає працювати. Нам потрібно знайти інше рішення.

9.6. Скрипти [import_06]

Image

Примітка: папки [06, dir1, dir2] було розміщено в папці [Sources Root] проекту PyCharm. Папки [dir1, dir2] ідентичні тим, що наведено в попередніх прикладах.

Файл [config.json] має такий вигляд:


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

Ми вводимо два типи шляхів:

  • абсолютні шляхи, рядки 7–8;
  • відносні шляхи, рядки 3–6. Вони відносяться до кореневого каталогу, вказаного в рядку 2. Таким чином, коли проект переміщується в інше місце, потрібно змінити лише цей рядок;

Скрипт [utils.py] використовує файл [config.json] і формує шлях Python:


# імпорт
import codecs
import json
import os
import sys

# конфігурація додатка
def config_app(config_filename: str) -> dict:
    # config_filename: ім’я файлу конфігурації
    # дозволяти передачу винятків

    # обробка файлу конфігурації
    with codecs.open(config_filename, "r""utf-8"as file:
        config = json.load(file)
    # додавання залежностей до sys.path
    rootDir = config['rootDir']
    # додаємо відносні залежності проекту до syspath
    for directory in config['relativeDependencies']:
        # додаємо залежність на початок syspath
        sys.path.insert(0f"{rootDir}/{directory}")
    # додаємо до syspath абсолютні залежності проекту
    for directory in config['absoluteDependencies']:
        # додаємо залежність на початок syspath
        sys.path.insert(0, directory)
    # створюється словник конфігурації
    return config

# папку, з якої виконується скрипт
def get_scriptdir():
    return os.path.dirname(os.path.abspath(__file__))
  • рядок 8: функція [config_app] отримує як параметр ім’я конфігураційного файлу;
  • рядки 12–14: файл конфігурації використовується для створення словника [config];
  • рядок 20: [sys.path] — це список папок Python Path;
  • рядки 17–20: відносні залежності з конфігураційного файлу додаються до Python Path. Вони додаються на початок масиву [sys.path], рядок 20. Адже коли Python шукає модуль, він послідовно обходить папки з [sys.path]. Однак у цьому документі модулі з однаковими іменами будуть розташовані в різних папках [sys.path]. Розмістивши залежності додатка на початку масиву [sys.path], ми гарантуємо, що вони будуть обшукані раніше, ніж інші папки [sys.path], які можуть містити модулі з однаковими іменами;
  • рядки 21–24: абсолютні залежності конфігураційного файлу додаються до Python Path;
  • рядок 26: повертається конфігурація додатка;
  • рядки 29–30: функція [get_scriptdir] повертає абсолютний шлях до папки скрипта, що виконується (тобто того, в якому знаходиться виклик функції);

Головний скрипт [main] має такий вигляд:


# імпортування
import sys

from utils import config_app


def affiche_path(msg: str):
    # повідомлення
    print(f"{msg}------------------------------")
    # sys.path
    for path in sys.path:
        print(path)


# main -------------
try:
    # sys.path налаштовано
    affiche_path("avant....")
    config = config_app(f"{get_scriptdir()}/config.json")
    affiche_path("après....")
    # імпортуємо module4
    from module4 import f4
    # виконання f4
    f4()
except BaseException as erreur:
    print(f"L'erreur suivante s'est produite : {erreur}")
finally:
    print("done")
  • рядок 4: імпортується функція [config_app]. Слід зауважити, що оскільки [utils] та [main] знаходяться в одній папці, ця функція [import] працює завжди. Дійсно, папка головного скрипта автоматично додається до Python Path;
  • рядки 7–12: функція [affiche_path] виводить список папок у Python Path;
  • рядок 19: налаштовуємо додаток. Зверніть увагу, що до функції [config_app] передаємо абсолютний шлях до файлу конфігурації. Після цієї інструкції шлях Python Path було відновлено;
  • рядок 22: імпортується [module4]. Завдяки відновленню Python Path цей модуль буде знайдено;
  • рядок 24: виконується функція [f4];

У контексті PyCharm результати виконання такі:


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

Коментарі

  • рядки 2–13: Python Path для PyCharm. У ньому містяться всі папки, додані до [Sources Root] цього проєкту;
  • рядки 14–29: Python Path, сформований функцією [config_app]. У рядках 15–16 містяться дві залежності, які ми додали;
  • рядки 22–27: системні папки інтерпретатора Python, який виконав скрипт;
  • рядки 28–29: виконання відбувається нормально;

Тепер перейдемо до контексту, який до цього моменту викликав помилку виконання:

  • запускаємо термінал Python;
  • переходимо в папку, відмінну від тієї, що містить виконуваний скрипт;

(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

Цього разу все гаразд (рядки 20–21). Зауважимо, що [sys.path] не містить тих самих папок, що й під час виконання під PyCharm.

9.7. Скрипти [import_07]

Ми вдосконалюємо попереднє рішення двома способами:

  • замінюємо файл конфігурації [config.json] на скрипт [config.py]. Справа в тому, що файл jSON має суттєву проблему: його неможливо коментувати. Словник [config.json] можна замінити на словник Python, який має ту перевагу, що його можна коментувати;
  • ми використовуємо модуль, доступний для всіх Python-проектів на цій машині;

9.7.1. Встановлення модуля, доступного на всьому комп’ютері

Image

Вище ми створюємо папку [packages/myutils] у проєкті PyCharm (назви не мають значення).

Скрипт [myutils.py] має такий вигляд:


# імпорт
import sys
import os


def set_syspath(absolute_dependencies: list):
    # absolute_dependencies: список абсолютних імен папок

    # додаємо до syspath абсолютні залежності проекту
    for directory in absolute_dependencies:
        # перевіряється наявність папки
        existe = os.path.exists(directory) and os.path.isdir(directory)
        if not existe:
            # генерується виняток
            raise BaseException(f"[set_syspath] le dossier du Python Path [{directory}] n'existe pas")
        else:
            # додаємо папку на початок syspath
            sys.path.insert(0, directory)
  • рядки 6–18: функція [set_syspath] створює Python Path із списком папок, які передаються їй як параметр;
  • рядки 12–15: перевіряється, чи існує папка, яку потрібно додати до Python Path;

Скрипт [__init.py__] (дві риски перед і після імені; це обов’язкова умова) має такий вигляд:


from .myutils import set_syspath

Імпортуємо функцію [set_syspath] зі скрипта [myutils]. Позначення [.myutils] вказує на шлях [./myutils], тобто скрипт [myutils], що знаходиться в тій самій папці, що й [__init.py]. Можна було б використати позначення [myutils]. Однак ми створимо модуль [myutils] з областю дії на рівні машини. Тому позначення [from myutils import set_syspath] стало б неоднозначним. Чи йдеться про імпорт скрипта [myutils] із поточної папки, чи скрипта [myutils], що має область дії на рівні машини? Позначення [.myutils] усуває цю неоднозначність.

Скрипт [setup.py] (тут також ім’я є фіксованим) має такий вигляд:


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)

У цьому скрипті описується модуль, який ми збираємося створити. У даному випадку ми створимо його локально. Але той самий процес використовується для створення офіційно розповсюджуваного модуля (див. |pypi|). Важливими моментами тут є наступні:

  • рядок 3: назва створюваного модуля;
  • рядок 4: версія модуля;
  • рядок 5: його опис;
  • рядки 7–8: автор модуля;

Щоб встановити цей модуль із охопленням на рівні машини, слід виконати такі дії:

Image

Потім у терміналі Python вводять таку команду: [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

Відтепер будь-який скрипт машини може імпортувати модуль [myutils], навіть якщо він не входить до коду проєкту.

9.7.2. Скрипт [config.py]

Image

Скрипт [config.py] забезпечує налаштування додатка:


def configure():
    import os

    # абсолютний шлях до папки конфігураційного скрипта
    script_dir = os.path.dirname(os.path.abspath(__file__))
    # абсолютні шляхи до папок, які потрібно додати до syspath
    absolute_dependencies = [
        # локальні папки
        f"{script_dir}/dir1",
        f"{script_dir}/dir2",
    ]
    # оновлення syspath
    from myutils import set_syspath
    set_syspath(absolute_dependencies)

    # повертаємо конфігурацію
    return {}
  • рядок 1: функція [configure] забезпечує налаштування додатка;
  • рядки 7–10: словник, який раніше містився у [config.json];
  • рядки 9–10: оскільки ми перебуваємо у скрипті, ми можемо безпосередньо використовувати абсолютні імена папок [dir1, dir2];
  • рядки 12–14: використовуємо функцію [set_syspath] з модуля [myutils], який ми щойно створили, щоб визначити Python Path конфігурації;
  • рядок 20: повертаємо словник конфігурації додатка. Тут він порожній;

9.7.3. Скрипт [main.py]

Головний скрипт [main] має такий вигляд:


# налаштовуємо додаток
import config

config = config.configure()

# syspath налаштовано — можна виконувати імпорт
from module4 import f4

# main -------------
try:
    f4()
except BaseException as erreur:
    print(f"L'erreur suivante s'est produite : {erreur}")
finally:
    print("done")
  • рядки 2–4: налаштовуємо додаток за допомогою модуля [config.py]. Доступ до нього є, оскільки він знаходиться в тій самій папці, що й головний скрипт. А папка головного скрипта завжди входить до Python Path;
  • коли доходимо до рядка 6, Python Path вже сформовано з урахуванням папки модуля [module4]. Отже, його можна імпортувати в рядку 7;
  • рядки 10–15: залишається лише виконати функцію [f4];

Результати виконання в PyCharm такі:

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

У терміналі Python, розташованому поза папкою головного скрипта, результати такі:

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

Відтепер ми завжди будемо діяти таким самим чином для налаштування додатка:

  • наявність скрипта [config.py] у папці головного скрипта. Цей скрипт містить функцію [configure], яка має два завдання:
    • сформувати Python Path для додатка. Для цього [config.py] визначає всі папки, що містять модулі, які використовує додаток, і формує Python Path із їхніми абсолютними іменами;
    • сформувати словник [config] конфігурації додатка;

Ми застосовуємо цю схему до другої версії практичного завдання. Адже, як ми пам’ятаємо, версія 1 працювала в середовищі PyCharm, але не в терміналі Python. Проблема полягала в Python Path.