Laajoissa ja monimutkaisissa ohjelmistoprojekteissa kaiken koodin tuottaminen itse ei ole enää käytännöllistä. Ei ole esimerkiksi järkevää, että jokaisessa ohjelmistoprojektissa toteutetaan oma ohjelmointirajapinta tietokantaoperaatioille, tai sovelluskehys koodin testaamiseen. Jotta pyörää ei tarvitsisi aina keksiä uudelleen, ovat ohjelmistokehittäjät kehittäneet valtavan määrän avoimen lähdekoodin kirjastoja, joita jokainen voi hyödyntää projekteissaan.

Kirjastojen lähdekoodi on usein luettavissa versionhallinta-alustoilla, kuten GitHubissa. Usein kirjastoja päivitetään jatkuvasti ja nämä päivitykset synnyttävät kirjastoista uusia versioita. Kirjastojen versioita julkaistaan erilaisiin rekistereihin, joista ne ovat helposti asennettavissa. The Python Package Index (PyPI) on eräs tämän kaltainen, Python-kirjastoille tarkoitettu rekisteri.

Projektissa käytettävät kirjastojen versiot ovat projektin riippuvuuksia. Riippuvuuksia asennetaan Python-projekteissa tyypillisesti projektikohtaisiin virtuaaliympäristöihin, jottei samalla tietokoneella olevien projektien riippuvuuksissa syntyisi ristiriitoja. Jotta riippuvuuksien ja virtuaaliympäristöjen hallinta sujuisi helposti, käytämme kurssilla uv-komentorivityökalua. uv on nopea, Rustilla toteutettu työkalu, joka hoitaa samalla kertaa niin Python-versioiden, virtuaaliympäristöjen kuin riippuvuuksienkin hallinnan.

Miksi uv?

Python-ekosysteemissä on tarjolla useita kilpailevia työkaluja riippuvuuksien ja virtuaaliympäristöjen hallintaan. Tunnetuimpia ovat mm. suoraan Pythonin mukana tulevat pip ja venv, sekä kolmannen osapuolen työkalut Poetry, Pipenv ja conda.

  • pip + venv ovat Pythonin standardikirjaston työkaluja, eli mitään ylimääräistä ei tarvitse asentaa. Työkalujen käyttö on kuitenkin melko käsityötä: virtuaaliympäristö pitää itse luoda ja aktivoida, riippuvuudet listataan usein pelkkään requirements.txt-tiedostoon ilman kunnollista versiolukkoa, eikä työkalu osaa hallita käytettävää Python-versiota.
  • Poetry toi Python-maailmaan kunnollisen riippuvuuslukituksen (poetry.lock) ja siistin pyproject.toml-pohjaisen projektinhallinnan, mutta on huomattavasti uv:tä hitaampi, eikä osaa asentaa tai hallita Python-versioita itse.
  • conda on suunniteltu erityisesti data-analytiikan ja tieteellisen laskennan tarpeisiin, ja osaa asentaa myös ei-Python-riippuvuuksia (esim. C-kirjastoja), mutta on raskas ja hidas yleiskäyttöiseen sovelluskehitykseen.

uv:n suurimmat edut kilpailijoihinsa nähden ovat:

  • Nopeus. uv on toteutettu Rustilla ja on riippuvuuksien asennuksessa tyypillisesti kymmeniä kertoja nopeampi kuin pip tai Poetry.
  • Kaikki yhdessä työkalussa. uv hoitaa sekä Python-versioiden, virtuaaliympäristöjen että riippuvuuksien hallinnan, eikä erillisiä työkaluja (esim. pyenv) tarvita.
  • Yksinkertainen, standardeja noudattava projektirakenne. uv käyttää Pythonin virallisen PEP 621 -standardin mukaista pyproject.toml-muotoa, mikä tekee projekteista yhteensopivia myös muiden työkalujen kanssa.
  • Aktiivinen kehitys. uv on tällä hetkellä Python-yhteisössä nopeimmin yleistyvä riippuvuudenhallintatyökalu, ja sen taustalla oleva Astral kehittää myös muita suosittuja Python-työkaluja, kuten Ruff-linteriä.

Huomioita komennoista

Monilla tietokoneilla Python-version kolme komennot suoritetaan python3-komennolla komennon python sijaan. Tarkista käytössä oleva versio komennolla:

python3 --version

Jos komentoa python3 ei jostain syystä löydy, tarkista python-komennon käyttämä versio komennolla:

python --version

Jos molemmissa tapauksissa versio on alle 3.14, ei hätää: uv osaa itse asentaa ja hallita Python-versioita, joten erillistä Python-asennusta ei välttämättä tarvita, katso alempaa.

Kurssilla käytetään uv:n versiota 0.12 (tai uudempaa). Jos koneellasi on vanhempi versio, se on syytä päivittää komennolla uv self update!

Asennus

Ennen kuin pääsemme tutustumaan uv:n käyttöön tarkemmin, tulee se ensin asentaa. Seuraa alla olevista ohjeista tietokoneesi käyttöjärjestelmälle sopivaa asennusohjetta, kannattaa toki vilkaista myös uv:n virallinen asennusohje.

HUOM: kaikki asennustavat saattavat vaatia terminaali-ikkunan sulkemisen ja uudelleen avaamisen, jotta uv:n komennot alkavat toimia.

Linux- ja macOS-asennus

Asenna uv suorittamalla terminaalissa seuraava komento:

curl -LsSf https://astral.sh/uv/install.sh | sh

Asennusskripti lisää uv-binäärin polun automaattisesti PATH-muuttujaan (tyypillisesti hakemisto $HOME/.local/bin). Käynnistä terminaali uudestaan ja varmista, että asennus onnistui suorittamalla komento uv --version. Komennon pitäisi tulostaa asennettu versio.

Windows-asennus

Asenna uv suorittamalla PowerShellissä seuraava komento:

powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

Käynnistä terminaali uudestaan ja varmista, että asennus onnistui suorittamalla komento uv --version. Komennon pitäisi tulostaa asennettu versio.

Python-version hallinta

uv osaa myös asentaa Python-versioita itse, erillistä Python-asennusta ei siis välttämättä tarvita. Voit asentaa esimerkiksi Python-version 3.14 komennolla:

uv python install 3.14

Asennetut versiot näkee komennolla:

uv python list

Kun projekti alustetaan alla kuvatulla tavalla vaatimalla tietty Python-versio, uv asentaa version automaattisesti tarvittaessa, eikä erillistä uv python install-komennon suorittamista välttämättä tarvita.

Projektin alustaminen

Harjoitellaan uv:n käyttöä tekemällä pieni esimerkkiprojekti. Luo hakemisto uv-testi haluamaasi hakemistoon. Avaa hakemisto terminaalissa ja suorita siellä komento:

uv init --python 3.14 --no-package

Komennon yhteydessä annettu --python 3.14-asetus asettaa projektin Python-version vaatimukseksi vähintään version 3.14. uv ei kysy komennon suorittamisen yhteydessä kysymyksiä, vaan luo tarvittavat tiedostot suoraan valmiiksi täytettynä.

--no-package-asetus on tärkeä: ilman sitä uv init alustaa projektin oletusarvoisesti asennettavaksi paketiksi, jolloin se luo mm. src-hakemistoon oman alihakemiston projektin nimellä, sekä pyproject.toml-tiedostoon [build-system]-osion. Tällä kurssilla teemme projekteista yksinkertaisempia sovelluksia, emme julkaistavia paketteja, joten --no-package pitää projektin rakenteen siistinä.

Komennon suorittamisen jälkeen hakemistoon ilmestyy muutama tiedosto: pyproject.toml, .python-version, README.md sekä main.py. Tiedosto .python-version kertoo, mitä Python-versiota projektissa oletusarvoisesti käytetään. Tiedoston pyproject.toml sisältö on kutakuinkin seuraava:

[project]
name = "uv-testi"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.14"
dependencies = []

Osiossa [project] näemme mm. uv init-komennon suorituksen yhteydessä asettamamme Python-version vaatimuksen, joka on muotoa requires-python = ">=3.14". Merkintä tarkoittaa, että projektin käyttö vaatii vähintään Python-version 3.14. Kohta dependencies puolestaan tulee sisältämään projektin riippuvuudet, kun niitä lisätään.

Poistetaan lopuksi vielä tiedostoon uv init-komennon luoma esimerkkitiedosto main.py turhana, sillä kirjoitamme koodimme myöhemmin hakemistoon src.

Riippuvuuksien asentaminen

Varoitus: pip

Olet saattanut asentaa Pythonin tarvitsemia riippuvuuksia pip-komennolla. Älä käytä pipiä tällä kurssilla sillä jos teet niin, teet 99.9% todennäköisyydellä jotain väärin.

Tällä kurssilla riippuvuudet asennetaan uv:lla.

Asennetaan seuraavaksi projektiimme ensimmäisen riippuvuus. Riippuvuuksien löytäminen onnistuu helpoiten Googlettamalla ja etsimällä hakutuloksista sopivia GitHub-repositorioita, tai PyPI-sivuja. Asennetaan esimerkkinä projektiimme cowsay-kirjasto. Tämä onnistu projektin juurihakemistossa (samassa hakemistossa, missä pyproject.toml-tiedosto sijaitsee) komennolla:

uv add cowsay

Asennuksen komento on siis muotoa uv add <kirjasto>. Komennon suorittamisen jälkeen huomaamme, että pyproject.toml-tiedoston dependencies-kohtaan on ilmestynyt uutta sisältöä:

dependencies = [
    "cowsay>=6.1",
]

uv add-komento asentaa oletusarvoisesti kirjaston uusimman version, joka oli komennon suoritushetkellä 6.1. Usein tämä on juuri se, mitä haluamme tehdä. Voimme kuitenkin asentaa halutessamme esimerkiksi cowsay-kirjaston version 5.0 komennolla:

uv add cowsay==5.0

Jos haluaisimme poistaa kirjaston projektimme riippuvuuksien joukosta, se onnistuisi komennolla:

uv remove cowsay

Pidetään kuitenkin cowsay-kirjasto toistaiseksi asennettuna.

Riippuvuuksien lisäämisen ja poistamisen yhteydessä uv päivittää projektin virtuaaliympäristön automaattisesti (hakemistoon .venv, joka luodaan projektin juureen) sekä tiedoston uv.lock. Tiedosto sisältää kaikkien asennettujen riippuvuuksien tarkat versiotiedot, joiden avulla uv pystyy aina asentamaan täsmälleen samat versiot. Tästä syystä tiedosto tulee lisätä versionhallintaan.

Hakemistoa .venv ei sen sijaan tule tallentaa versionhallintaan, eli se on syytä lisätä heti tiedostoon .gitignore.

Jos haluat pelkästään varmistaa, että kaikki pyproject.toml-tiedostossa määritellyt riippuvuudet on asennettu (esim. kloonattuasi jonkun toisen tekemän projektin), onnistuu se komennolla:

uv sync

Komento tekee tarvittaessa myös virtuaaliympäristön alustamisen. Käytännössä komentoa ei useinkaan tarvitse suorittaa erikseen, sillä esimerkiksi seuraavassa kappaleessa esiteltävä uv run-komento synkronoi riippuvuudet automaattisesti ennen suoritusta.

Komentojen suorittaminen virtuaaliympäristössä

Lisätään seuraavaksi uv-testi-hakemistoon hakemisto src ja sinne tiedosto index.py. Lisätään tiedostoon seuraavat koodirivit:

import cowsay

cowsay.tux("uv is awesome!")

Koodissa käytämme import-lausetta saadaksemme cowsay-kirjaston käyttöömme. Jos suoritamme tiedoston terminaalissa komennolla:

python3 src/index.py

On lopputuloksena seuraava virheilmoitus:

ModuleNotFoundError: No module named 'cowsay'

Tämä johtuu siitä, että emme ole projektin virtuaaliympäristön sisällä, jonka vuoksi Python ei löydä projektimme riippuvuuksia. Asia korjaantuu käyttämällä run-komentoa:

uv run python3 src/index.py

uv run-komento siis suorittaa annetun komennon virtuaaliympäristössä, jonka sisällä Python löytää riippuvuutemme. Komento myös varmistaa ennen suoritusta, että riippuvuudet ovat ajan tasalla (eli tekee tarvittaessa uv sync-komennon suorittaman toimenpiteen).

Kun projektia kehitetään aktiivisesti ja komentoja suoritetaan terminaalissa jatkuvasti, voi olla kätevää olla koko ajan virtuaaliympäristön sisällä, sen sijaan että jokaisen komennon eteen kirjoittaa uv run. Tämä onnistuu aktivoimalla virtuaaliympäristö suoraan komennolla:

source .venv/bin/activate

(Windowsilla vastaava komento on .venv\Scripts\activate.)

Kun olemme virtuaaliympäristössä, komentorivin syöterivin edessä on suluissa virtuaaliympäristön nimi:

$ (uv-testi)

Virtuaaliympäristön sisällä voimme suorittaa komennon “normaalisti”, eli ilman uv run-komentoa:

python3 src/index.py

Voimme lähteä virtuaaliympäristöstä komennolla deactivate.

Kehityksaikaiset riippuvuudet

uv:n avulla riippuvuuksia on mahdollista ryhmitellä niiden käyttötarkoituksen mukaan. Melko yleinen tapa ryhmitellä riippuvuuksia on ryhmitellä ne kehityksen ja suorituksen aikaisiksi riippuvuuksiksi. Kehitysaikaisia riippuvuuksia tarvitaan ohjelmiston kehityksen aikana, mutta ne eivät ole välttämättömiä ohjelman suorituksessa.

Komennon uv add suorittaminen asentaa oletusarvoisesti riippuvuudet tiedoston dependencies-kohtaan. Näiden riippuvuuksien lisäksi voimme asentaa projektiimme riippuvuuksia, joita tarvitsemme vain kehityksen aikana. Näitä riippuvuuksia ovat kaikki ne, joita itse sovelluksen käynnistäminen (esimerkiksi python3 src/index.py-komennon suorittaminen) ei tarvitse.

Kehityksenaikaisten riippuvuuksien asentaminen onnistuu antamalla uv add-komennolle --dev-flagi. Esimerkiksi pian tutuksi tulevan pytest-kirjaston voi asentaa kehityksaikaiseksi riippuvuudeksi seuraavalla komennolla:

uv add pytest --dev

Komennon suorittaminen lisää pytest-kirjaston riippuvuudeksi pyproject.toml-tiedoston [dependency-groups]-osion dev-ryhmään:

[dependency-groups]
dev = [
    "pytest>=9.1.1",
]

Kehityksenaikaisten riippuvuuksien määritteleminen on kätevää, koska se vähentää asennettavien riippuvuuksien määrää tapauksessa, jossa haluamme vain suorittaa sovelluksen. Tässä tilanteessa riippuvuuksien asentamisen voi tehdä komennolla uv sync --no-dev.

HUOM: uv run synkronoi riippuvuudet automaattisesti ennen suoritusta, ja tekee tämän oletusarvoisesti kehitysaikaiset riippuvuudet mukaan lukien. Jos siis olet asentanut riippuvuudet komennolla uv sync --no-dev, palauttaa pelkkä uv run-komento kehitysaikaiset riippuvuudet takaisin asennetuksi. Jos haluat suorittaa komennon ilman kehitysaikaisia riippuvuuksia, käytä komentoa uv run --no-dev.

Varoitus: pip

Olet saattanut asentaa Pythonin tarvitsemia riippuvuuksia pip-komennolla. Älä käytä pipiä tällä kurssilla sillä jos teet niin, teet 99.9% todennäköisyydellä jotain väärin.

Tällä kurssilla riippuvuudet asennetaan uv:lla.

Ratkaisuja yleisiin ongelmiin

Virtuaaliympäristö on sekaisin

Jos kohtaat oudon virheen, jonka epäilet johtuvan rikkoutuneesta virtuaaliympäristöstä tai riippuvuuksista, poista virtuaaliympäristö ja lukkotiedosto ja asenna riippuvuudet uudelleen, eli anna komennot:

rm -rf .venv
rm uv.lock
uv sync

Yritä tämän jälkeen uudelleen!

uv ei löydä oikeaa Python-versiota

Jos uv valittaa, ettei se löydä pyproject.toml-tiedostossa vaadittua Python-versiota, asenna vaadittu versio uv:n itsensä avulla:

uv python install 3.14

ja suorita tämän jälkeen uv sync uudelleen.

uv:n itsensä päivittäminen

Jos epäilet käytössäsi olevan vanhentuneen uv:n version aiheuttavan ongelmia, päivitä se komennolla:

uv self update

ja yritä tämän jälkeen epäonnistunutta komentoa uudelleen.