Kodėl priklausomybių valdymas tapo tokia problema
Kas dirba su Python projektais, tas puikiai žino tą jausmą, kai kažkas komandoje paleidžia kodą ir viskas veikia, o tau – ne. Arba kai projektas veikė prieš mėnesį, o dabar staiga meta keisčiausias klaidas. Dažniausiai kaltas priklausomybių chaosas.
Python ekosistemoje priklausomybių valdymas ilgai buvo skausmingas klausimas. Pip, nors ir oficialus įrankis, vienas neišsprendžia visų problemų. Todėl atsirado alternatyvos – Pipenv ir Poetry. Bet kuri iš šių priemonių geriausia? Ar apskritai verta keisti įprastą Pip į kažką naujo?
Šiame straipsnyje išnagrinėsiu visas tris populiariausias Python priklausomybių valdymo sistemas. Ne teoriškai, o praktiškai – kaip jos veikia realiuose projektuose, kokios jų stipriosios ir silpnosios pusės, ir svarbiausia – kurią pasirinkti konkrečiai situacijai.
Pip – senbuvis, kuris vis dar laiko pozicijas
Pip yra standartinis Python paketų valdymo įrankis. Jis ateina kartu su Python instaliacija ir veikia paprastai: pip install package_name ir viskas. Bet štai kur prasideda problemos.
Pip pats savaime nesukuria izoliuotų aplinkų. Jei įdiegsi paketą globaliai, jis bus prieinamas visur sistemoje. Skamba gerai, kol nepradedi dirbti su keliais projektais, kuriems reikia skirtingų Django ar NumPy versijų. Tada prasideda pragaras.
Todėl Pip paprastai naudojamas kartu su virtualenv arba venv – įrankiais, kurie sukuria izoliuotas Python aplinkas. Tipiškas workflow atrodo taip:
python -m venv myenv
source myenv/bin/activate
pip install django requests pandas
Priklausomybes galima išsaugoti į requirements.txt failą komanda pip freeze > requirements.txt. Vėliau kitas žmogus gali jas įsidiegti su pip install -r requirements.txt.
Problema ta, kad pip freeze išsaugo VISAS priklausomybes, įskaitant sub-priklausomybes. Jei įdiegei Django, kuris priklauso nuo sqlparse, asgiref ir dar kelių paketų, visi jie atsidurs requirements.txt. Tai sukuria kelis sunkumus:
- Sunku atskirti, kuriuos paketus tu tiesiogiai naudoji, o kurie yra tik priklausomybės
- Versijų konfliktai gali kilti netikėtai, kai atnaujini vieną paketą
- Reprodukuoti tiksliai tą pačią aplinką skirtinguose kompiuteriuose tampa sudėtinga
Dar viena Pip problema – nėra tikro dependency resolution mechanizmo. Jei paketas A reikalauja bibliotekos X versijos 1.0, o paketas B reikalauja X versijos 2.0, Pip tiesiog įdiegs paskutinę nurodytą versiją ir tikėsis, kad viskas veiks. Spoiler: dažnai neveikia.
Tačiau Pip turi ir privalumų. Jis paprastas, greitas, universalus. Visi Python programuotojai jį žino. Dokumentacija ir Stack Overflow pilna atsakymų. Mažiems projektams ar greituose prototipuose Pip su requirements.txt visiškai pakanka.
Pipenv – pirmasis rimtas iššūkis
Pipenv atsirado apie 2017-uosius ir kurį laiką buvo oficialiai rekomenduojamas Python Packaging Authority. Jis bandė išspręsti pagrindines Pip problemas vienu įrankiu.
Pagrindinė Pipenv idėja – sujungti virtualenv ir pip funkcionalumą į vieną komandą. Užuot rankiniu būdu kūręs virtualią aplinką, tiesiog rašai pipenv install django ir Pipenv automatiškai:
- Sukuria virtualią aplinką (jei jos dar nėra)
- Įdiegia Django
- Išsaugo priklausomybę į Pipfile
- Sugeneruoja Pipfile.lock su tiksliais versijų hash’ais
Pipfile yra daug skaidesnis už requirements.txt. Jis atskiria tiesiogines priklausomybes nuo sub-priklausomybių ir leidžia nurodyti skirtingas priklausomybes development ir production aplinkoms:
[packages]
django = "*"
requests = ">=2.25.0"
[dev-packages]
pytest = "*"
black = "*"
Pipfile.lock failas – tai JSON formatu išsaugota visa priklausomybių medžio struktūra su tiksliais hash’ais. Tai užtikrina, kad kiekvienas komandos narys ar CI/CD sistema įdiegs TIKSLIAI tas pačias paketo versijas. Jokių netikėtumų.
Pipenv taip pat turi įmontuotą dependency resolver’į. Jei bandysi įdiegti nesuderinamus paketus, Pipenv praneš apie konfliktą iš anksto, o ne runtime metu.
Dar viena smagi funkcija – pipenv graph komanda, kuri parodo vizualų priklausomybių medį. Labai patogu suprasti, kodėl projektas turi tiek daug paketų.
Bet Pipenv turi ir trūkumų. Didžiausia problema – našumas. Dependency resolution procesas gali užtrukti LABAI ilgai, ypač dideliuose projektuose. Kartais pipenv install gali kaboti 10-15 minučių, bandydamas išspręsti priklausomybių konfliktus.
Kita problema – Pipenv vystymas sulėtėjo. Nuo 2020-ųjų atnaujinimai tapo reti, kai kurios klaidos neištaisomos mėnesius. Bendruomenė pradėjo ieškoti alternatyvų, ir čia į sceną įžengė Poetry.
Poetry – naujos kartos sprendimas
Poetry atsirado maždaug tuo pačiu metu kaip Pipenv, bet pastaraisiais metais tapo aiškiu favoritu. Ir tam yra priežasčių.
Poetry filosofija – būti visapusišku projekto valdymo įrankiu, ne tik priklausomybių tvarkytoju. Jis padeda ne tik įdiegti paketus, bet ir sukurti projekto struktūrą, build’inti, publikuoti į PyPI.
Pradėti naują projektą su Poetry labai paprasta:
poetry new myproject
Tai sukuria pilną projekto struktūrą su tinkamais katalogais, README, pyproject.toml failu ir testų direktorija. Jei jau turi esamą projektą, gali jį inicializuoti:
poetry init
Ši komanda interaktyviai paklaus apie projekto detales ir sukurs pyproject.toml – modernų Python projekto konfigūracijos failą, kuris palaiko PEP 518 standartą.
Priklausomybių įdiegimas atrodo pažįstamai:
poetry add django
poetry add --dev pytest
Poetry automatiškai atnaujina pyproject.toml ir generuoja poetry.lock failą. Šis lock failas panašus į Pipfile.lock, bet greičiau generuojamas ir patikimesnis.
Vienas didžiausių Poetry privalumų – greitis. Dependency resolution yra DAUG greitesnis nei Pipenv. Tai, kas Pipenv užtrunka 10 minučių, Poetry atlieka per minutę ar dvi. Tai ypač jaučiasi dideliuose projektuose su šimtais priklausomybių.
Poetry taip pat turi puikų virtualių aplinkų valdymą. Pagal nutylėjimą jis sukuria virtualią aplinką projekto kataloge arba centralizuotoje vietoje (konfigūruojama). Aktyvuoti aplinką galima su:
poetry shell
Arba paleisti komandą tiesiogiai virtualioje aplinkoje:
poetry run python script.py
Dar viena svarbi funkcija – projekto build’inimas ir publikavimas. Jei kuri biblioteką, kurią nori įkelti į PyPI, Poetry tai supaprastina iki:
poetry build
poetry publish
Nereikia rašyti setup.py, konfigūruoti setuptools ar kitų sudėtingų dalykų. Viskas aprašoma pyproject.toml faile.
Poetry taip pat turi gerą dependency groups palaikymą. Gali sukurti ne tik dev priklausomybes, bet ir bet kokias kitas grupes:
poetry add --group docs sphinx
poetry add --group test pytest coverage
Tai leidžia tiksliau kontroliuoti, kas įdiegiama skirtingose situacijose.
Realūs naudojimo scenarijai ir rekomendacijos
Teorija teorija, bet praktikoje svarbu suprasti, kada naudoti kurį įrankį. Štai keletas konkrečių scenarijų.
Mažas projektas ar greitas prototipas
Jei kuri kažką greitai, eksperimentuoji ar projektas turi 5-10 priklausomybių, Pip su requirements.txt visiškai pakanka. Nereikia overengineerinti. Sukuri virtualią aplinką su venv, įdiegia ką reikia, išsaugoji requirements.txt ir viskas.
python -m venv venv
source venv/bin/activate
pip install flask requests
pip freeze > requirements.txt
Paprasta, greita, veikia. Visiems suprantama.
Vidutinis projektas su komanda
Kai projektas auga, komandoje dirba keli žmonės, ir pradedi jausti Pip apribojimus, laikas pereiti prie Poetry. Jis suteiks:
- Tikslų priklausomybių atkartojimą per poetry.lock
- Greitą ir patikimą dependency resolution
- Aiškų atskyrimą tarp production ir dev priklausomybių
- Gerą integraciją su CI/CD sistemomis
Poetry tapo de facto standartu daugelyje šiuolaikinių Python projektų. GitHub’e matai pyproject.toml failą – žinai, kad projektas naudoja Poetry.
Legacy projektas su Pipenv
Jei paveldėjai projektą, kuris jau naudoja Pipenv, nebūtinai reikia skubėti pereiti prie Poetry. Jei Pipenv veikia ir komanda su juo patenkinta, neverta keisti dėl pačio keitimo.
Tačiau jei susiduri su lėtumu ar kitomis Pipenv problemomis, migracija į Poetry nėra sudėtinga. Yra įrankiai, kurie automatiškai konvertuoja Pipfile į pyproject.toml.
Bibliotekos kūrimas
Jei kuri Python biblioteką, kurią planuoji publikuoti PyPI, Poetry yra akivaizdus pasirinkimas. Jis supaprastina visą build ir publish procesą, palaiko versijų valdymą, automatizuoja daug rutininių užduočių.
Pipenv nėra skirtas bibliotekų kūrimui – jis orientuotas į aplikacijas. Pip su setup.py veikia, bet reikalauja daug rankinio darbo.
Korporacinė aplinka su specifiniais reikalavimais
Didelėse organizacijose gali būti specifinių reikalavimų – privatus PyPI serveris, proxy nustatymai, saugumo politikos. Pip čia turi pranašumą, nes jis labiausiai paplitęs ir turi daugiausiai enterprise palaikymo.
Bet Poetry ir Pipenv taip pat palaiko privačius repository ir proxy nustatymus. Poetry tai daro per pyproject.toml:
[[tool.poetry.source]]
name = "private"
url = "https://pypi.company.com/simple"
Našumo palyginimas ir techniniai niuansai
Našumas – svarbus aspektas, ypač dideliuose projektuose. Atlikau paprastą testą su projektu, turinčiu ~50 tiesiogines priklausomybes (Django, Celery, DRF, pandas ir kt.).
Šviežio įdiegimo laikas (be cache):
- Pip: ~45 sekundės
- Poetry: ~2 minutės
- Pipenv: ~8 minutės
Pakartotinio įdiegimo laikas (su cache):
- Pip: ~15 sekundių
- Poetry: ~30 sekundių
- Pipenv: ~3 minutės
Pip greičiausias, bet tai suprantama – jis nedaro dependency resolution. Poetry vidutinis, bet priimtinas. Pipenv… na, jis lėtas. Labai lėtas.
Kitas svarbus aspektas – disk space naudojimas. Poetry ir Pipenv sukuria virtualias aplinkas, kurios užima vietą. Jei dirbi su daugeliu projektų, tai gali sukaupti gigabaitus.
Poetry turi naudingą funkciją – centralizuotą cache sistemą. Jei keli projektai naudoja tą patį paketą, jis saugomas vieną kartą ir tik linkinamas į projekto aplinką. Tai sutaupo vietos.
Dar vienas techninis niuansas – Python versijų palaikymas. Poetry leidžia nurodyti, kokias Python versijas projektas palaiko:
[tool.poetry.dependencies]
python = "^3.8"
Tai reiškia, kad projektas veiks su Python 3.8 ir naujesnėmis versijomis. Poetry automatiškai patikrina, ar tavo Python versija tinkama.
Pipenv taip pat palaiko Python versijos nurodymą Pipfile, bet ne taip lankščiai. Pip apskritai nesirūpina Python versija – tai tavo problema.
Integracija su kitais įrankiais ir ekosistema
Šiuolaikiniame Python projekte priklausomybių valdymas – tik viena dalis. Dar reikia testų, linting, formatting, CI/CD. Kaip skirtingi įrankiai integruojasi su platesne ekosistema?
Docker integracija
Visi trys įrankiai veikia su Docker, bet yra niuansų. Su Pip Dockerfile atrodo paprastai:
FROM python:3.11
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
Su Poetry reikia šiek tiek daugiau darbo, nes Poetry pats yra priklausomybė:
FROM python:3.11
RUN pip install poetry
COPY pyproject.toml poetry.lock .
RUN poetry install --no-root --no-dev
COPY . .
Arba galima naudoti Poetry eksportą į requirements.txt:
poetry export -f requirements.txt --output requirements.txt --without-hashes
Tada Docker build’as tampa paprastesnis ir greitesnis.
CI/CD sistemos
GitHub Actions, GitLab CI, Jenkins – visos šios sistemos palaiko visus tris įrankius. Bet Poetry turi geriausią palaikymą su oficialiais action’ais ir plugin’ais.
Pavyzdys GitHub Actions su Poetry:
- name: Install Poetry
uses: snok/install-poetry@v1
- name: Install dependencies
run: poetry install
IDE integracija
PyCharm, VS Code, vim su LSP – visi palaiko virtualias aplinkas, sukurtas bet kuriuo įrankiu. Bet Poetry ir Pipenv turi geresnę integraciją, nes IDE automatiškai aptinka jų aplinkas.
VS Code su Python extension automatiškai siūlo Poetry aplinką, jei projekto root’e yra pyproject.toml. PyCharm taip pat puikiai dirba su Poetry.
Pre-commit hooks
Pre-commit framework puikiai veikia su visais įrankiais. Bet jei naudoji Poetry, gali aprašyti pre-commit kaip dev priklausomybę:
poetry add --group dev pre-commit
Tada komandos nariai automatiškai gauna tą pačią pre-commit versiją.
Kas toliau ir į ką atsižvelgti renkantis
Python priklausomybių valdymo pasaulis nuolat evoliucionuoja. Neseniai atsirado PDM – dar vienas įrankis, kuris naudoja PEP 582 standartą ir visai nenaudoja virtualių aplinkų. Hatch – kitas naujas žaidėjas, integruotas į PyPA ekosistemą.
Bet šiandien Poetry yra saugiausias pasirinkimas daugumai projektų. Jis subrendęs, aktyviai vystomas, turi didelę bendruomenę ir puikią dokumentaciją. Jei pradedi naują projektą, rekomenduoju Poetry.
Pip vis dar aktualus mažiems projektams, skriptams, mokymosi tikslams. Jis paprastas ir visada veiks. Nereikia jo ignoruoti tik todėl, kad atsirado naujesnių įrankių.
Pipenv… na, jis vis dar naudojamas daugelyje projektų, bet naujiems projektams jo rekomenduoti negaliu. Jei tavo projektas jau naudoja Pipenv ir viskas veikia – puiku. Bet jei renkiesi naują įrankį, Poetry yra geresnis pasirinkimas.
Svarbiausia – pasirinkti įrankį, kuris atitinka tavo projekto poreikius. Mažam skriptui nereikia Poetry. Didelei aplikacijai nepakanka Pip. Bibliotekos kūrimui reikia build įrankių, kuriuos turi Poetry.
Ir nepamirsk, kad įrankis – tai tik įrankis. Svarbiausias dalykas – kodo kokybė, testai, dokumentacija. Gali turėti tobulą priklausomybių valdymą su Poetry, bet jei kodas šlamštas, niekas nepadės. Priklausomybių valdymas turėtų palengvinti gyvenimą, o ne tapti tikslu savaime.
