У темі 16 ми розклали програму по файлах
і простежили, як import шукає модуль: спершу sys.modules, потім вбудовані,
потім теки зі списку sys.path, остання з яких — site-packages. Саме туди
складає файли pip. Тоді ми дійшли до межі стандартної бібліотеки й зупинилися на
питанні: як узяти чужий пакет. Відповідь «встанови його» звучить просто рівно доти, доки проєкт
у тебе один.
Ця тема — про те, що стається з другим проєктом. І про інструмент, який робить із «стається» звичайну нудну рутину з трьох команд.
01 / БільДва проєкти й одна тека
Уяви Олену. Торік вона написала «Афішу» — маленьку програму, що збирає розклад заходів.
Афіша користується бібліотекою zvitnyk версії 1.4: там є функція
zvit(dani), яка повертає готовий рядок, і Афіша одразу робить із
ним .strip().
Цього тижня Олена почала новий проєкт — «Звіти». Він теж потребує zvitnyk,
але версії 2.0: у другій версії автори переробили функцію, і тепер zvit(dani)
повертає словник із розібраними полями. Звіти беруть звідти
["pidsumok"].
Обидва проєкти запускаються тим самим python3. Отже, обидва беруть пакети з
однієї й тієї самої теки site-packages. А там zvitnyk може лежати
рівно один: імпорт іде за іменем, а ім'я в пакета одне. Двох тек zvitnyk
поруч не буває — друге встановлення просто затирає перше.
Покрути повзунок і подивись, що з цього виходить. Перемикач праворуч показує ту саму історію у двох світах: без ізоляції й з нею.
Інтерактив 1 · Дві версії, одна тека
Чотири дії по черзі. Стеж не за командою, а за станом обох проєктів унизу.
Висновок напрошується сам: проєктам треба дати різні місця для пакетів.
Не різні комп'ютери, не різні операційні системи — саме різні теки, з яких Python читатиме
бібліотеки. Механізм, що це робить, зветься віртуальне середовище
(virtual environment), а модуль для його створення — venv.
02 / ОзначенняЩо таке середовище фізично
Слово «віртуальне» звучить загадково, тому одразу приземлимо. Віртуальне середовище — це звичайна тека на диску. Усередині неї три речі, і жодної магії:
- Власний
site-packages— порожня тека, у якуpipскладатиме пакети саме цього проєкту. - Власний
pythonу теціbin(на Windows —Scripts). Це не копія інтерпретатора, а символьне посилання або тонка обгортка на вже встановлений Python. - Файл
pyvenv.cfg— три рядки тексту, які кажуть: «мій справжній Python лежить ось тут». Саме за цим файлом інтерпретатор розуміє, що він працює всередині середовища.
Чого в середовищі немає — теж важливо. Немає стандартної бібліотеки: усі ті
math, json і pathlib беруться зі справжнього Python, на
який вказує pyvenv.cfg. Тому порожнє середовище важить кілька мегабайтів, а не
сотню, і створюється за секунди.
Поклацай по вузлах дерева — праворуч побачиш, що це й навіщо:
Інтерактив 2 · Що всередині теки .venv
Реальна структура після python3 -m venv .venv. Обери файл або теку.
03 / ПрактикаСтворити, активувати, вийти
Команда рівно одна, і ти вже бачила її форму в темі
02: прапорець -m означає «знайди модуль із такою назвою й виконай його як
програму».
python3 -m venv .venv
Кілька секунд — і поруч з'явилась тека .venv. Ім'я не обов'язкове, але це
усталена домовленість: крапка на початку робить теку прихованою в Unix, а однакова назва в усіх
проєктах дозволяє редакторам знаходити середовище автоматично. Одне середовище — один проєкт;
ділити одне на два проєкти означає повернути собі рівно ту проблему, заради якої все й почалося.
Активація
Середовище створене, але оболонка про нього ще не знає. Активація — це
запуск невеликого скрипта, який лежить усередині .venv. Команда залежить від
оболонки, а не від настрою:
| оболонка | команда активації | де це буває |
|---|---|---|
| bash / zsh | source .venv/bin/activate | Linux, macOS |
| fish | source .venv/bin/activate.fish | Linux, macOS |
| PowerShell | .venv\Scripts\Activate.ps1 | Windows |
| cmd.exe | .venv\Scripts\activate.bat | Windows |
Ознака успіху одна: перед запрошенням у терміналі з'явився префікс (.venv).
Вихід — команда deactivate без аргументів; вона працює в будь-якій оболонці й
повертає все як було. Закрити вікно термінала теж достатньо: активація живе рівно стільки,
скільки живе цей процес оболонки.
running scripts is disabled on this system.
Лікується один раз на весь профіль користувача:
Set-ExecutionPolicy -Scope CurrentUser RemoteSigned. Друга: у PowerShell не працює
source — там просто пишуть шлях до скрипта, як у таблиці вище..venv
записані абсолютні шляхи — і в pyvenv.cfg, і в перших рядках виконуваних
файлів pip. Перейменував теку проєкту — середовище зламалося. Це не трагедія:
воно створюється за секунди й повністю відновлюється з requirements.txt, про який
ми говоритимемо далі. Правильна реакція — видалити й створити заново, а не лагодити.04 / МеханікаАктивація — це зсув PATH
Ось місце, де більшість людей уявляє собі щось містичне: «середовище ввімкнулося». Насправді скрипт активації робить рівно три дрібниці:
- дописує теку
.venv/binна початок змінноїPATH; - заводить змінну
VIRTUAL_ENVзі шляхом до середовища — щоб інші програми могли про нього дізнатися; - дописує
(.venv)у рядок запрошення, щоб ти бачила, де перебуваєш.
Усе. Жодних змін у самому Python, жодного «режиму». Згадай схему з
теми 02: коли ти пишеш слово в терміналі,
оболонка проходить теки зі списку PATH згори вниз і бере перший
збіг. Активація просто ставить теку середовища попереду всіх — і тепер перший збіг завжди її.
Інтерактив 3 · Куди дивиться оболонка після активації
Той самий пошук по PATH, що й у темі 02. Змінюється лише перший рядок списку.
source. Якби ти запустила
activate як звичайну програму, вона стартувала б окремим процесом, змінила
PATH у себе — і померла разом зі своїми змінами. Слово source означає
«виконай цей файл у моїй оболонці, не породжуючи нову». Звідси й побічний ефект, який
дивує: deactivate — не програма й не файл на диску. Це функція оболонки, яку
створив скрипт активації. Пошук which deactivate нічого не знайде.Активація не обов'язкова
Практичний наслідок, який рятує в скриптах і на сервері: якщо активація — це лише зручний спосіб знайти файл, його можна назвати й напряму.
↑ працює без жодної активації — узятий саме той інтерпретатор
.venv/bin/python -m pip install pandas
↑ і pip теж той, що треба
Саме так роблять у файлах автоматизації та в cron: там немає інтерактивної
оболонки, і активувати нема кому. Форма -m pip замість голого pip
знову з теми 02 — вона знімає всі питання про те, чий саме pip запустився.
05 / НаслідокЯк змінюється sys.path
Тепер з'єднаємо це з темою 16. Маршрут пошуку модуля не змінився: sys.modules,
вбудовані, теки з sys.path. Змінився вміст останнього пункту —
і тільки він.
Коли інтерпретатор стартує, він дивиться, чи лежить поруч із ним pyvenv.cfg.
Якщо лежить — Python оголошує себе «інтерпретатором середовища»: кладе в sys.prefix
шлях до .venv, а справжній корінь ховає в sys.base_prefix. Стандартна
бібліотека береться за другим, пакети — за першим.
Інтерактив 4 · sys.path до і після
Той самий комп'ютер, три різні інтерпретатори. Порівняй останні рядки списку.
хто ти такий
sys.path
pip — це звичайний модуль Python. Він дивиться на sys.prefix
того інтерпретатора, яким його запустили, і складає файли туди. Тому .venv/bin/pip
ставить у середовище, а /usr/bin/pip3 — у систему. І тому діагностика при будь-якій
плутанині завжди одна: which python і python -c "import sys;
print(sys.prefix)".06 / Інструментpip: чотири команди щодня
pip — це менеджер пакетів Python. Він ходить у сховище
PyPI (Python Package Index), завантажує звідти пакет, розпаковує його в
site-packages і повторює те саме для всіх його залежностей. Команд у нього
десятки, у щоденній роботі потрібні чотири.
| команда | що робить | деталь, яку варто знати |
|---|---|---|
| pip install pandas | ставить пакет і всі його залежності | залежностей зазвичай більше, ніж здається: pandas тягне ще щонайменше три |
| pip list | показує, що вже стоїть | у свіжому середовищі список майже порожній — це нормально |
| pip show pandas | картка пакета | рядок Location каже, у яку саме теку він ліг; Required-by — хто на нього спирається |
| pip uninstall pandas | прибирає пакет | залежності не прибирає — вони лишаються сиротами |
Дві дрібниці, які економлять час. pip install -U pandas оновлює вже
встановлений пакет до найновішої версії. А pip install pandas==2.1.4 ставить точно
названу — і ось тут починається окрема історія.
07 / ЧислаВерсії й обмеження
Версія пакета зазвичай має три числа: 2.1.4. Домовленість зветься
семантичне версіонування і читається так:
- перше число (major) змінюється, коли зламали сумісність. Код, написаний під 1.x, під 2.0 може перестати працювати — і це дозволено правилами;
- друге (minor) — додали щось нове, старе працює як раніше;
- третє (patch) — полагодили помилку, більше нічого не змінилось.
Тому в файлі залежностей пишуть не просто ім'я, а обмеження (specifier). Форм кілька, і різниця між ними — не стилістична:
| запис | читається як | коли доречно |
|---|---|---|
| zvitnyk | будь-яка версія | майже ніколи: завтра прилетить 3.0 і зламає все |
| zvitnyk==1.4.2 | рівно ця, жодної іншої | файл, що фіксує стан для відтворення |
| zvitnyk>=1.4 | ця або новіша | небезпечно саме по собі: верхньої межі немає |
| zvitnyk>=1.4,<2.0 | у межах першої гілки | найчастіший вибір для бібліотеки |
| zvitnyk~=1.4.2 | >=1.4.2, але менше за 1.5.0 | «приймаю лише виправлення помилок» |
Остання форма — «сумісний реліз» — плутає найчастіше, бо її поведінка залежить від того,
скільки чисел ти написала. ~=1.4.2 дозволяє мінятися лише третьому числу, а
~=1.4 — уже другому. Правило просте: останнє написане число може зростати,
попереднє — ні. Перевір це очима:
Інтерактив 5 · Яку версію обере pip
Список усіх релізів пакета zvitnyk на PyPI. Обери запис у requirements.txt.
08 / Пам'ятьrequirements.txt
Середовище зроблене, пакети стоять, усе працює. Проблема в тому, що це знання живе тільки на твоєму комп'ютері. Колега клонує проєкт — і має голий код без жодної бібліотеки. Ти сама через пів року на новому ноутбуці — у тій самій ситуації.
Розв'язок — текстовий файл зі списком того, що треба поставити. За домовленістю він зветься
requirements.txt і лежить у корені проєкту. Дві команди на все:
pip freeze > requirements.txt
# відтворити те саме в новому середовищі
pip install -r requirements.txt
Кутова дужка > — це не частина pip, а можливість оболонки: «те, що команда
надрукувала б на екран, поклади у файл». Сам pip freeze просто друкує список.
Формат файлу — по рядку на пакет, і freeze завжди пише жорстке
==:
pandas==2.2.3
python-dateutil==2.9.0
pytz==2024.2
tzdata==2024.2
Тут п'ять рядків, хоча ставила ти два пакети. Решта — залежності залежностей, і це правильно:
відтворювати треба весь стан, а не тільки те, що пам'ятаєш. Порожні рядки й рядки з
# у файлі ігноруються, тож коментарі писати можна.
Навіщо фіксувати версії
А тепер головне питання теми: чому ==, а не просто ім'я пакета? Відповідь видно
лише в майбутньому, тому повзунок в інтерактиві — це час. Уяви, що колега ставить залежності
не сьогодні, а через кілька місяців:
Інтерактив 6 · Той самий файл через пів року
Код не змінювався. Змінилось лише те, що встиг випустити автор бібліотеки.
pip freeze. Він фіксує усе, зокрема
випадково поставлені пакети й залежності залежностей. Через рік такий файл уже не відповідає на
питання «що мій проєкт справді використовує». Тому зрілі проєкти тримають два списки: короткий,
написаний руками («мені потрібні pandas і matplotlib»), і довгий згенерований, що фіксує точні
версії всього. У сучасних інструментах перший — це pyproject.toml, другий —
lock-файл; про них у розділі для досвідчених.09 / Помилкаexternally-managed-environment
Тепер найпоширеніша сучасна помилка. Людина ставить Python на свіжому Ubuntu чи Fedora, пише
pip install requests — і замість встановлення отримує сторінку тексту, яка
починається зі слова error.
Це не поломка. Це захист, який працює саме так, як задумано.
Причина в тому, що на Linux і macOS частина операційної системи написана на Python: менеджер
пакетів, службові утиліти, налаштування мережі. Усі вони імпортують бібліотеки із системного
site-packages. Коли pip ставить туди свою версію якоїсь бібліотеки, він
перезаписує ту, на яку розраховував менеджер пакетів, — і той перестає працювати. Полагодити це
буває складніше, ніж перевстановити систему.
Тому з PEP 668 дистрибутиви кладуть поруч зі своїм Python файл-маркер
EXTERNALLY-MANAGED, а pip, побачивши його, відмовляється щось ставити. Наведи мишею
на частини повідомлення — і перевір праворуч усі чотири варіанти реакції:
Інтерактив 7 · Розбір повідомлення й правильна реакція
Справжній вивід pip 25 на Ubuntu. Наводь на фрагменти; перемикачем обери, що робити.
sudo pip install гірший за все інше. Слово
sudo прибирає останній запобіжник — права доступу. Наслідків три, і кожен
неприємний. Перший: файли лягають у системні теки, і менеджер пакетів ОС про них не знає — при
наступному оновленні виникає конфлікт, який нічим не розв'язати. Другий: якщо pip
перезапише бібліотеку, від якої залежить сам менеджер пакетів, ти втратиш і спосіб це полагодити.
Третій, найтихіший: під sudo код зі стороннього пакета виконується від імені
адміністратора вже під час встановлення. Проста вимога, яка знімає всі три: pip ніколи
не запускають через sudo.10 / ГігієнаЧому теку не комітять
Тека .venv лежить усередині проєкту — здається логічним покласти її в репозиторій
разом із кодом. Так робити не треба, і причин чотири.
- Це похідний файл. Середовище повністю відновлюється з
requirements.txtза одну команду. Зберігати те, що обчислюється, — та сама помилка, що зберігати__pycache__з теми 16. - Абсолютні шляхи. Ми вже бачили: усередині записано, де саме на твоєму диску лежить Python. На чужій машині таке середовище не запуститься.
- Двійкові файли під конкретну систему. Частина пакетів містить
скомпільований код:
.soна Linux,.pydна Windows. Вони не переносяться між системами навіть теоретично. - Розмір. Середовище з
pandasіmatplotlib— це сотні мегабайтів дрібних файлів. Репозиторій із ними стає незручним назавжди: git зберігає історію, і видалення теки пізніше вагу вже не поверне.
Тому в корені проєкту заводять файл .gitignore — список того, що git має
не помічати:
.venv/
__pycache__/
*.pyc
У репозиторій потрапляє натомість requirements.txt: кілька сотень байтів замість
кількох сотень мегабайтів, і при цьому вони описують той самий стан. Це і є правильний обмін.
11 / ГлибшеДля тих, хто вже писав код
Далі — сучасний шар, який новачку сьогодні не потрібен, але з яким доведеться зустрітися в будь-якому живому проєкті.
pyproject.toml замість requirements.txt
requirements.txt — формат без стандарту: це просто список рядків для pip. Сучасна
заміна описана в PEP 621 і зветься pyproject.toml. Це один файл, у якому лежить усе:
ім'я проєкту, версія, залежності й налаштування інструментів.
name = "afisha"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
"pandas>=2.2,<3.0",
"matplotlib>=3.9",
]
Головна відмінність не в синтаксисі, а в ролі. requirements.txt відповідає на
питання «що поставити в це середовище». pyproject.toml відповідає на питання «чим
є цей проєкт» — і тому його розуміє не тільки pip, а й складальники пакетів, форматувальники,
лінтери. Обидва живуть поруч без конфлікту: перший фіксує точні версії, другий описує наміри.
pip install -e .
Крапка означає «пакет, що лежить у поточній теці», а прапорець -e (editable) —
«не копіюй файли в site-packages, а постав туди посилання на них». Наслідок дуже
приємний: твій власний пакет стає імпортованим з будь-якої теки, при цьому правки в коді
підхоплюються одразу, без перевстановлення.
Це правильна відповідь на класичне «мій модуль не імпортується з підтеки». Дописувати теки
руками в sys.path — милиця, яка ламається при першому ж переносі проєкту.
Встановлення в режимі редагування розв'язує те саме без жодного рядка коду.
Три інструменти, про які ти почуєш
uv— швидкий менеджер, написаний на Rust: замінює собоюvenv,pipі lock-файли одразу, а встановлення виконує в десятки разів швидше за pip.poetry— керує залежностями й публікацією пакета цілком: сам створює середовище, розв'язує конфлікти версій і тримає власний lock-файл.pipx— ставить утиліти командного рядка (black,ruff, той самийpoetry) кожну у власне приховане середовище, щоб вони були доступні всюди й не змішувалися з пакетами проєктів.
pip freeze
записує версії, але не записує вміст. Lock-файл (uv.lock,
poetry.lock) додає до кожного пакета контрольну суму архіву. Різниця стає видною в
неприємний день: якщо хтось підмінить уже випущену версію на PyPI, freeze цього не помітить,
а перевірка хешу — помітить і зупинить встановлення.Дрібниці, які тішать
python3 -m venv --upgrade-deps .venv одразу створює середовище зі свіжим pip —
інакше перша ж команда нагадає, що всередині лежить версія з дня випуску твого Python.
pip install --dry-run показує, що було б встановлено, нічого не змінюючи.
pip check перевіряє, чи не суперечать одна одній версії вже встановлених пакетів.
А pip download складає архіви в теку — так переносять залежності на машину без
інтернету.
12 / ПідсумокЩо ми тепер уміємо
Ми навчилися давати кожному проєкту власне місце для бібліотек — і закрили цілий клас проблем, які інакше з'їдають дні:
- чому це потрібно — в одній теці лежить лише одна версія пакета, тож без ізоляції другий проєкт завжди ламає перший;
- що таке середовище — тека з власним
site-packages, власнимpythonі файломpyvenv.cfg; стандартна бібліотека лишається спільною; - що робить активація — дописує теку в початок
PATH, і нічого більше; без активації працює прямий шлях.venv/bin/python; - що змінюється всередині — лише хвіст
sys.path: пакети беруться з середовища, стандартна бібліотека — з базового Python; - pip і версії —
install,list,show,uninstall; з усіх відповідних версій завжди береться найновіша, тому обмеження — це стеля; - відтворюваність —
pip freezeу файл,pip install -rз файлу, і жорстке==, щоб через пів року поставилось те саме; - PEP 668 — системний Python захищений навмисно, і правильна реакція на
externally-managed-environment— створити середовище, а не обійти захист.
І питання, яке з цього виростає. Ми весь час говорили про те, що щось ламається:
пакет не тієї версії, файл не знайшовся, функція повернула не те. Досі така поломка означала
кінець програми — traceback і зупинка. Але падати не обов'язково: Python уміє
ловити помилку й вирішувати, що з нею робити. Навіть звичний
ModuleNotFoundError — це насправді об'єкт, який можна перехопити й обробити.
Про це — тема 18, помилки та винятки.
practice.ipynb ти створиш справжнє
віртуальне середовище прямо із зошита, зазирнеш у його pyvenv.cfg, порівняєш
sys.path двох інтерпретаторів на власні очі, збереш і прочитаєш
requirements.txt — і наприкінці приберешся за собою, не лишивши на диску нічого.Далі в темі
Теорію прочитано. Тепер закріпи її на практиці.