Skip to content

Poetry

In de vorige hoofdstukken heb je gewerkt met een eigen conda environment zodat je jouw pythonomgeving mooi gescheiden kan houden van andere studenten die op dezelfde computer werken en voor het isoleren van de verschillende projecten waar je aan werkt. Dit is echt de oplossing voor alle problemen waarbij volledige Pythoninstallaties onbruikbaar kunnen worden — waarna je alles opnieuw moet installeren.

Opnieuw beginnen of nieuwe environments aanmaken heeft wel een nadeel: je moet alle packages die je nodig hebt opnieuw installeren. Welke waren dat ook alweer? Vast numpy, en matplotlib, en…? Niet handig. Als je code gaat delen met elkaar krijg je regelmatig te maken met een ImportError waarna je weer één of ander package moet installeren.

Nu pythondaq netjes is uitgesplitst in een MVC-structuur en de wijzigingen met Git worden bijgehouden, ga je er een package van maken zodat je het ook met anderen kan delen.

Packages op PyPI (de standaardplek waar Python packages gepubliceerd worden) geven altijd hun dependencies op. Dat zijn de packages die verder nog nodig zijn om alles te laten werken. Installeer je matplotlib, dan krijg je er six, python-dateutil, pyparsing, pillow, numpy, kiwisolver, cycler automatisch bij. Maar alleen de namen van packages zijn niet genoeg. Welke versies van numpy werken met de huidige versie van matplotlib? Allemaal zaken die je — als je een package schrijft — zelf moet bijhouden. Het voordeel is dat jouw gebruikers alleen maar jouw pakket hoeven te installeren — de rest gaat vanzelf.

En… hoe test je je package zodat je zeker weet dat hij het bij een ander ook doet? Heel vaak werkt het bij jou wel, maar vergeet je een bestand mee te sturen dat wel echt nodig is.1 Of: bij jou werkt import my_new_cool_app.gui wel, maar bij een ander geeft hij een ImportError. De bestanden zijn er wel, maar worden verkeerd geïmporteerd.

Hoe krijg je eigenlijk je code bij iemand anders? Liefst als één bestand, of zelfs met pip install my_new_cool_app; dat zou wel mooi zijn.

En daar is Poetry.

Er zijn meerdere tools ontwikkeld om dezelfde problemen op te lossen. Poetry is heel populair geworden. Het richt zich op het officiële ecosysteem: standaard Python packages, ofwel PyPI en pip; niet conda (zie meer hierover in paragraaf pip vs conda). Jammer, maar dit zorgt er wel voor dat iedereen mét of zónder Anaconda je package kan installeren. Dat is dan wel weer fijn. Wij gaan Anaconda gebruiken om een virtual environment met alleen Python te maken. Vervolgens installeren we alles dat we nodig hebben met pip. Dat werkt prima, want we mengen verder geen conda met pip packages. Het maken van conda packages valt daarmee buiten het bestek van deze cursus, al is dat een relatief kleine stap als je je standaard Python package af hebt.

Werken in een terminal

Poetry is een tool die je enkel en alleen in de terminal kunt gebruiken. Het heeft alleen een command-line interface (CLI). Ben je nog niet zo bekend met het navigeren in een terminal dan kun je als oefening de Terminal Adventure Game spelen.

Poetry installeren

Om Poetry te installeren gaan we gebruik maken van pipx, zie voor meer informatie paragraaf pipx. Eerst moeten we pipx installeren

  1. Open een Anaconda Prompt.
  2. Maak een nieuwe environment en installeer pipx via pip
    Terminal
    conda create --name pipx python
    
    Terminal
    conda activate pipx
    
    Terminal
    python -m pip install --user pipx
    
  3. Zorg ervoor dat de map waarin pipx apps opslaat, is opgenomen in je PATH omgevingsvariabele.
    Terminal
    python -m pipx ensurepath
    
  4. Sluit de Anaconda Prompt en open een nieuwe.
  5. Test of pipx nu werkt met:
    Terminal
    pipx
    

Nu kunnen we Poetry installeren met pipx.

  1. Installeer Poetry met behulp van pipx.
    Terminal
    pipx install poetry
    
  2. Test of poetry nu werkt met:
    Terminal
    poetry
    
  3. Activeer een andere environment en test of Poetry ook daar werkt.

We gaan Poetry bedienen door commando's te geven in de terminal van Visual Studio Code. We laten de terminal weten welk programma wij willen gaan besturen, door poetry in te typen. En daarachter wat we willen dat Poetry gaat doen. We kunnen informatie over Poetry opvragen met het commando about.

(ecpc) > poetry about 

Poetry about

Open een terminal en vraag informatie over Poetry op met het commando poetry about. Lees de tekst die Poetry aan je teruggeeft, waar kan je meer informatie vinden?

Nieuw Poetry project

Info

We gaan werken met modules en packages. Ben je daar nog niet zo bekend mee, zorg dan dat je paragraaf Modules en paragraaf packages gemaakt hebt.

Stel je wilt een package schrijven met wat handige functies om veelgebruikte statistische berekeningen makkelijk uit te voeren. Je noemt het easystat. Het doel is eerst om het in al je eigen analyses makkelijk te kunnen gebruiken (import easystat) maar je wilt het ook op GitHub zetten en wie weet vinden anderen het ook handig! Je wilt het dus ook netjes doen. En niet later van anderen horen: leuk, maar bij mij werkt het niet!

Easystat Poetry project aanmaken

Een project stop je altijd in een map , als je aan Poetry vraagt om een project te maken zal er een nieuwe (project)map worden aangemaakt. Je denkt na over een geschikte locatie en besluit dat de projectmap in de ECPC map moet komen te staan. Je opent Visual Studio Code en opent de map ECPC. Je opent een terminal en controleert dat de terminal ook in de map ECPC is. Je geeft Poetry de opdracht om een nieuw project met de naam easystat aan te maken in de src-layout10 met het commando poetry new --src easystat. Je bekijkt de nieuw gemaakte mappenstructuur en ziet dat het overeenkomt met de mappenstructuur zoals hieronder weergegeven:

ECPC
├── oefenopdrachten
├── pythondaq
├── easystat
           ├── src
                      └── easystat
                                 └── __init__.py
           ├── tests
                      └── __init__.py
           ├── pyproject.toml
           └── readme.md
└── •••

src-layout

Door het project in een source layout (src-layout) te bouwen maken we het expres iets moeilijker om vanuit een script je package te importeren. Je kunt dat dan alleen nog maar doen door het package zelf ook te installeren (zoals andere gebruikers ook moeten doen) en daardoor loop je zelf tegen eventuele problemen aan. Werkt het uiteindelijk bij jou? Dan werkt het ook bij andere mensen.

Testcode

(ecpc) > poetry new --src easystat 

Checkpunten:

  • De projectmap easystat staat in de map ECPC.
  • In de projectmap easystat staat een map src.
  • In de map src staat een package map easystat

Projecttraject

  • Easystat Poetry project aanmaken
  • Easystat conda environment aanmaken
  • Easystat shortcuts.py en try_shortcuts.py aanmaken
  • Easystat try_shortcuts.py testen
  • Easystat Poetry install
  • Easystat dependencies toevoegen

Bekijk nog eens de mappenstructuur. Allereerst is er een projectmap easystat (waar de map src in staat) aangemaakt . Je kunt nu in GitHub Desktop deze map easystat toevoegen als nieuwe repository, zoals we gedaan hebben in opdracht Repository toevoegen.

Laten we één voor één kijken welke mappen en bestanden Poetry heeft aangemaakt. We zien een README.md in de projectmap staan. Hierin komt een algemene beschrijving van ons project.2

Daarna is er een map tests. Goede software wordt getest. In deze map komen bestanden te staan die delen van de code runnen en resultaten vergelijken met verwachte resultaten — zoals je kunt doen in opdracht Packages.3

Dan komt de src-map. Daarin komt ons nieuwe package easystat4 te staan. Er is alvast voor ons een __init__.py aangemaakt. Handig!

En als laatste… een pyproject.toml5 waarin alle informatie over je project wordt bijgehouden. Ook staat er in dit bestand informatie voor de verschillende tools die je kunt gebruiken. De inhoud van het bestand ziet er ongeveer zo uit:

[tool.poetry]
name = "easystat"
version = "0.1.0"
description = ""
authors = ["David Fokkema <davidfokkema@icloud.com>"]
readme = "README.md"
packages = [{include = "easystat", from = "src"}]

[tool.poetry.dependencies]
python = "^3.13"


[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

Het bestand is in het TOML-formaat.11 Tussen de vierkante haken staan de koppen van de verschillende secties in dit configuratiebestand. Overal zie je poetry terugkomen, want dat is de tool die wij gebruiken. In de eerste sectie staat informatie over ons project. Je kunt daar bijvoorbeeld een beschrijving toevoegen of het versienummer aanpassen. De tweede sectie bevat de dependencies. Dit zijn alle Pythonpackages die ons project nodig heeft. Op dit moment is dat alleen maar Python. Ook het versienummer van Python is belangrijk. Hier is dat 3.13 en het dakje geeft aan dat nieuwere versies 3.14, 3.15, enz. ook prima zijn, maar 3.12 (te oud) en 4.0 (te nieuw) niet. Dit kan belangrijk zijn. Gebruikers met een iets oudere versie van Python — bijvoorbeeld versie 3.11 — kunnen nu het package niet installeren. Als je niet per se de nieuwste snufjes van Python 3.13 nodig hebt kun je aangeven dat een iets oudere versie van Python ook prima is. Op dit moment — herfst 2024 — is Python 3.13 de nieuwste versie. Het is dus prima om minimaal 3.12 te vragen — die versie is inmiddels een jaar oud.

Conda environment aanmaken

Bij het schrijven van een nieuw package is het zéker belangrijk om een conda environment te gebruiken. Anders loop je het risico dat je package lijkt te werken maar bij iemand anders crasht. Immers, het kan best zijn dat jij NumPy gebruikt en al eerder geïnstalleerd had. Bij iemand die NumPy nog niet geïnstalleerd had gaat het dan mis.

Easystat conda environment aanmaken

Je voegt de projectmap easystat toe als existing/local repository in GitHub . Vanuit GitHub Desktop open je de repository easystat in Visual Studio Code. Je maakt in Anaconda Prompt een nieuwe conda environment aan met de naam easystat en daarin python=3.12 . Uiteraard selecteer je het nieuwe environment in Visual Studio Code.

Testcode

(easystat) > conda list 

Checkpunten:

  • De projectmap easystat is geopend in Visual Studio Code.
  • Python is geïnstalleerd in de conda environment easystat.
  • In Visual Studio Code is de conda environment easystat geactiveerd.

Projecttraject

  • Easystat Poetry project aanmaken
  • Easystat conda environment aanmaken
  • Easystat shortcuts.py en try_shortcuts.py aanmaken
  • Easystat try_shortcuts.py testen
  • Easystat Poetry install
  • Easystat dependencies toevoegen

conda-forge

Merk op dat we nu niet gebruik hoeven te maken van de conda-forge channel. Python zelf staat in alle kanalen en we gaan verder geen software installeren met conda, dus ook niet uit conda-forge.

Maken van de easystat-package

We starten met ons package. Stel, we berekenen vaak de standaarddeviatie van het gemiddelde en maken daarvoor een handige shortcut in shortcuts.py. Nu willen we deze shortcut ook in een ander script gebruiken. Dit kunnen we doen door package easystat te importeren in dit nieuwe script zodat we de functie stdev_of_mean daar ook kunnen gebruiken. We maken een script try_shortcuts.py om dit te testen.

Easystat shortcuts.py en try_shortcuts.py aanmaken

Maak zoals hieronder aangegeven de bestanden shortcuts.py en try_shortcuts.py aan:

shortcuts.py
import numpy as np 


def stdev_of_mean(values):
    """Calculate the standard deviation of the mean"""
    return np.std(values) / np.sqrt(len(values))    
try_shortcuts.py
from easystat.shortcuts import stdev_of_mean

print(f"{stdev_of_mean([1, 2, 2, 2, 3])=}")
easystat
├── src
           ├── easystat
                      ├── __init__.py
                      └── shortcuts.py
├── tests
           ├── __init__.py
           └── try_shortcuts.py
├── pyproject.toml
└── readme.md

Import numpy could not be resolved

Misschien is het je al opgevallen dat VS Code een oranje kringeltje onder numpy zet in de eerste regel. Als je daar je muiscursor op plaatst krijg je een popup met de melding Import numpy could not be resolved. Daar moeten we misschien wat mee en dat gaan we straks ook doen.

In de eerste regel van test_shortcuts.py importeren we de functie uit het nieuwe package om uit te proberen. In de laatste regel gebruiken we een handige functie van f-strings.6

Easystat try_shortcuts.py testen

Je bent heel benieuwd of je package al werkt. Je runt het bestand try_shortcuts.py en krijgt een foutmelding...

Testcode

try_shortcuts.py
from easystat.shortcuts import stdev_of_mean

print(f"{stdev_of_mean([1, 2, 2, 2, 3])=}")
(easystat) > python try_shortcuts.py

Checkpunten:

  • Je hebt de juiste conda environment geactiveerd.
  • Je runt het bestand try_shortcuts.py uit de map tests.
  • Je krijgt een foutmelding ModuleNotFoundError: No module named 'easystat'

Projecttraject

  • Easystat Poetry project aanmaken
  • Easystat conda environment aanmaken
  • Easystat shortcuts.py en try_shortcuts.py aanmaken
  • Easystat try_shortcuts.py testen
  • Easystat Poetry install
  • Easystat dependencies toevoegen

Dit konden we verwachten. We hebben onze package immers nog niet geïnstalleerd. Als we onze package gaan delen met andere mensen verwachten wij dat zij onze package ook gaan installeren, door dezelfde stappen te doorlopen als andere gebruikers komen we erachter of alles wel goed werkt.

Installeren van een package

Het installeren van de package kan makkelijk met Poetry:

(easystat) > poetry install 

Poetry is even bezig en ons package is geïnstalleerd.

Easystat Poetry install

Je gaat het project easystat installeren in de conda environment easystat met het commando poetry install. Waarschijnlijk krijg je een error (zie info-blok hieronder) maar door rustig te lezen los je die op. Je installeert alsnog het project easystat draai je opnieuw tests/try_shortcuts.py en zie je een nieuwe error verschijnen ModuleNotFoundError: No module named 'numpy'. Hoera de eerste error is met succes opgelost en je kunt door met de volgende opdracht.

Current Python version is not allowed by the project

Waarschijnlijk krijg je in dikke rode letters de error:

Current Python version (3.12.7) is not allowed by the project (^3.13).
Please change python executable via the "env use" command.
In de pyproject.toml staat bij de Python dependency dat er minstens versie 3.13 of hoger (^3.13) nodig is voor dit project7. En de conda environment easystat heeft Python 3.12 geïnstalleerd. Je kunt nu twee dingen doen:

  1. Je bedenkt dat voor dit project een lagere versie van Python ook voldoende is en past de Python versie dependency aan in de pyproject.toml naar ^3.12.
  2. Je vindt dat het project minstens versie 3.13 moet gebruiken en upgrade Python in de easystat environment met conda install python=3.13.

Testcode

(easystat) > conda list 

Checkpunten:

  • Je hebt de juiste conda environment geactiveerd.
  • Nadat je poetry install hebt gedaan krijg je de melding Installing the current project: easystat (0.1.0).
  • Je runt het bestand tests/try_shortcuts.py.
  • Je krijgt een foutmelding ModuleNotFoundError: No module named 'numpy'

Projecttraject

  • Easystat Poetry project aanmaken
  • Easystat conda environment aanmaken
  • Easystat shortcuts.py en try_shortcuts.py aanmaken
  • Easystat try_shortcuts.py testen
  • Easystat Poetry install
  • Easystat dependencies toevoegen

Als we het testscript nu draaien krijgen we wéér een foutmelding:

ModuleNotFoundError: No module named 'numpy'
Ons package heeft NumPy nodig en dat hebben we nog niet geïnstalleerd. Dat kunnen we handmatig doen maar dan hebben andere gebruikers een probleem. Veel beter is het om netjes aan te geven dat ons package NumPy nodig heeft — als dependency.

Dependencies toevoegen

Om een dependency aan te geven vertellen we Poetry dat hij deze moet toevoegen met:

(easystat) > poetry add numpy 

Easystat dependencies toevoegen

Je voegt Numpy als dependency toe aan het project easystat met het commando poetry add numpy. Je kijkt in de pyproject.toml en warempel daar staat Numpy nu bij de dependencies! Je vraagt je af of Numpy nu ook in de conda environment easystat is geïnstalleerd en controleert dit met conda list en waarachtig Numpy staat in de lijst . Weer ga je tests/try_shortcuts.py draaien en ditmaal krijg je een uitkomst!

Testcode

try_shortcuts.py
from easystat.shortcuts import stdev_of_mean

print(f"{stdev_of_mean([1, 2, 2, 2, 3])=}")
(ecpc) > python try_shortcuts.py

Checkpunten:

  • Je hebt de juiste conda environment geacitveerd.
  • Je hebt Numpy als dependency toegevoegd.
  • Je krijgt een uitkomst als je het bestand tests/try_shortcuts.py runt.

Projecttraject

  • Easystat Poetry project aanmaken
  • Easystat conda environment aanmaken
  • Easystat shortcuts.py en try_shortcuts.py aanmaken
  • Easystat try_shortcuts.py testen
  • Easystat Poetry install
  • Easystat dependencies toevoegen

Fijn! Het verwijderen van dependency PACKAGE gaat met poetry remove PACKAGE. Poetry heeft Numpy nu toegevoegd aan de environment easystat.Gewone package managers als Pip en Conda zullen geen packages toevoegen aan je Poetry project als je pip/conda install package aanroept. Gebruik daarom altijd poetry add package als je met Poetry aan een package werkt.

Info

Als we de code in ons package aanpassen dan hoeven we het niet opnieuw te installeren met Poetry, maar als we met de hand iets wijzigen in de pyproject.toml dan moet dat wel. Als je een ImportError krijgt voor je eigen package — bijvoorbeeld als je nieuwe mappen of bestanden hebt aangemaakt — probeer dan eerst voor de zekerheid poetry install.

Poetry.lock

Poetry.lock

Na het toevoegen van Numpy is er ook een bestand poetry.lock bijgekomen. Hierin staan de exacte versies van alle geïnstalleerde packages. Vaak wordt dit bestand gecommit zodat collega-ontwikkelaars exact dezelfde versies installeren zodra ze poetry install aanroepen. Om dat te proberen maken we even een schone conda environment:

Schone environment

  1. Maak een schone conda environment met conda create --name easystat python=3.12
  2. Kies voor ja als Conda een waarschuwing geeft dat deze environment al bestaat en vraagt of je het bestaande environment wilt verwijderen.
  3. Draai tests/try_shortcuts.py en bekijk de foutmelding.

We krijgen meteen foutmeldingen. Immers, we hebben nog niets geïnstalleerd.

Poetry.lock

  1. Installeer de easystat package met poetry.
  2. Waarvoor gebruikt Poetry de lock file (poetry.lock)?
  3. Draai tests/try_shortcuts.py en bekijk de uitkomst.
Wheels

Wheels

Wanneer we klaar zijn om ons package te delen met andere gebruikers gebruiken we het commando build om wheels te bouwen.

Bouw een wheel

  1. Bouw het wheel van easystat met poetry build.
  2. Bekijk de namen van de bestanden in de nieuwe map easystat/dist, welke extensie hebben ze?

(ecpc) > poetry build 

Een sdist is een source distribution. Een .tar.gz-bestand is een soort zipbestand met daarin de broncode van ons pakket. De tests worden daar niet in meegenomen. Een wheel is een soort bestand dat direct geïnstalleerd kan worden met pip. Zogenaamde pure-python packages bevatten alleen Pythoncode — en geen C-code die gecompileerd moet worden voor verschillende besturingssystemen of hardwareplatforms. Je herkent ze aan none-any in de bestandsnaam. None voor niet-OS-specifiek en any voor draait op elk hardwareplatform. We kunnen dit bestand als download neerzetten op een website of aan anderen mailen.

Test wheel

Laten we het wheel uitproberen. We gaan straks een nieuwe conda environment aanmaken, installeren het wheel en proberen het testscript te runnen — één keer vóór het installeren van het wheel en één keer ná het installeren, als volgt:

  1. Maak een nieuwe conda environment aan met de naam test-wheel en activeer deze.
    Terminal
    PS> conda create --name test-wheel python=3.12
    ...
    PS> conda activate test-wheel
    
  2. Draai tests/try_shortcuts.py en bekijk de foutmelding.
  3. Installeer het wheel met pip install dist/easystat-0.1.0-py3-none-any.whl.
  4. Draai tests/try_shortcuts.py en bekijk de uitkomst.

Het werkt! Je ziet dat pip install niet alleen ons package easystat installeert, maar ook de dependency numpy. Dat is precies wat we willen.

Het is belangrijk om de wheels niet in je GitHub repository te committen. Je repository is voor broncode, waarmee wheels gebouwd kunnen worden. Als je de stappen voor het aanmaken van de repository netjes gevolgd hebt dan heb je een .gitignore toegevoegd met Python-specifieke bestandsnamen en directories die genegeerd worden door Git en GitHub.

Poetry gebruiken voor een bestaand project

Met poetry new start je een nieuw project en maakt Poetry voor jou bestanden en mappen aan waarmee je aan de slag kunt. Maar vaak ben je al bezig met een project en wil je dat niet overschrijven. Ook is het een gedoe om een nieuw project te maken en daar je bestaande code in te kopieëren. Gelukkig kun je Poetry ook vertellen dat je al bezig bent en dat Poetry alleen een pyproject.toml-bestand moet aanmaken. Run dan in de map van je project:

Terminal
poetry init --no-interaction
Je geeft met poetry init de opdracht om Poetry alleen te initialiseren en --no-interaction voorkomt je dat je eerst honderd vragen krijgt over je project. Meestal kies je toch de standaardantwoorden.8

Info

Vergeet niet — waar nodig — de __init__.py bestanden toe te voegen aan de packages. Meer informatie over de __init__.py bestanden vind je in paragraaf packages.

Info

Als je al bezig bent met een project dan werk je als het goed is al in een conda environment. Daar heb je dan met conda install al packages geïnstalleerd die je nodig hebt. Het gebeurt dan makkelijk dat je vergeet om dat aan te geven met poetry add. Dat betekent alleen dat als iemand anders je package installeert dat er dependencies missen en dat jouw code dus niet werkt! Dit is makkelijk op te lossen. Zodra je Poetry gaat gebruiken wis dan je environment en maak een nieuwe aan met alleen Python. Dat gaat het makkelijkst als volgt. Stel dat je bezig bent in het environment pythondaq. We maken dan een nieuw environment met dezelfde naam:

(ecpc) > conda create --name pythondaq python=3.12 

Je overschrijft dus je huidige environment met een nieuwe, lege. Je kunt daarna met poetry add packages toevoegen net zo lang tot je geen ImportError meer krijgt.

Poetry flashingLED

Je gaat een bestaand project maken zodat je kunt oefenen om daar Poetry aan toe te voegen. Omdat de opdracht flashingLED een oefenopdracht was voor Pythondaq besluit je deze als oefenpackage te gebruiken. Je maakt een nieuwe repository flasher aan en opent deze in Visual Studio Code. Je maakt zelf in de repository flasher de src-layout van mappen en bestanden, zoals hier rechts is weergegeven. Het bestand flashingLED heb je gekopieerd uit je repository oefenopdrachten. Nu het oefenpackage klaar staat (commit) maak je een nieuwe conda environment met de naam flasher met daarin python=3.12 . Je activeert de environment flasher en voegt Poetry toe . Je installeert het pakket en daarna voeg je de benodigde dependencies toe (in ieder geval pyvisa-py maar wat nog meer?) net zolang tot het scriptje weer werkt .
ECPC
├── oefenopdrachten
├── pythondaq
├── flasher
           └── src
                      └── flasher
                                 ├── __init__.py
                                 └── flashingLED.py
└── •••

No module named 'serial'

Waarschijnlijk krijg je onder andere de foutmelding:

    ValueError: Please install PySerial (>=3.0) to use this resource type.
    No module named 'serial'
Super handig dat iemand daarboven heeft opgeschreven wat je moet doen om dit probleem op te lossen. Maar waarom moeten we nu ineens PySerial installeren9? Dat komt omdat we eerst pyvisa-py met conda uit de conda-forge channel installeerde en daar komt PySerial als dependencie mee. Nu installeerd Poetry met behulp van pip pyvisa-py en daar komt PySerial niet automatisch mee. En dus moeten we het nu zelf handmatig toevoegen.

Testcode

flasherLED.py
import pyvisa
import numpy as np
import time

rm = pyvisa.ResourceManager("@py")
ports = rm.list_resources()
print(ports)
device = rm.open_resource(
    "ASRL3::INSTR", read_termination="\r\n", write_termination="\n"
)

for value in np.arange(0, 10):
    device.query(f"OUT:CH0 {0}")
    time.sleep(1)
    device.query(f"OUT:CH0 {1023}")
    time.sleep(1)
(ecpc) > python flasherLED.py

Checkpunten:

  • Je hebt een repository flasher met daarin een src-layout.
  • Je hebt de juiste conda environment geactiveerd.
  • Poetry is toegevoegd aan het project.
  • Alle benodigde dependencies staan in het pyproject.toml en zijn geïnstalleerd in de conda environment.
  • Het runnen van flashingLED laat het LED knipperen.

Projecttraject

  • Communicatie met een meetinstrument: flashingLED
  • Versiebeheer met GitHub: Repository toevoegen
  • Poetry flashingLED

Poetry gebruiken voor pythondaq

Natuurlijk willen we Poetry ook gaan gebruiken bij pythondaq. Daarvoor moeten we twee dingen doen. Als eerste gaan we de pythondaq repository in een src-structuur zetten en daarna gaan we Poetry initialiseren.

Pythondaq: src-layout

Je project pythondaq is zo tof aan het worden dat je het met Poetry gaat beheren zodat jij en andere het gemakkelijk kunnen installeren en gebruiken. Om te beginnen zet je de repository om in een src-layout zoals hiernaast:
pythondaq
├──src
          └──pythondaq
                    ├──__init__.py
                    ├──arduino_device.py
                    ├──diode_experiment.py
                    └──view.py
└──README.md

Pythondaq: poetry

Nu de repository pythondaq in de src-layout staat voeg je Poetry toe om het project te beheren . Nadat alles gelukt is test je het project door een nieuwe conda environment aan te maken met de naam pythondaq met daarin alleen python=3.12 . Daarna installeer je het Poetry project en wanneer je view.py runt zie je als vanouds een lampje branden en een plot verschijnen.

Pseudo-code

(ecpc) > poetry install 

Checkpunten:

  • Je hebt Poetry geïnitialiseerd in de Pythondaq project map.
  • Na het initialiseren van Poetry is er een pyproject.toml in de projectmap aangemaakt.
  • Wanneer met poetry install in een nieuwe conda environment met alleen python=3.12 het pakket wordt geïnstalleerd werkt view.py daarna in die nieuwe omgeving naar behoren.

Projecttraject

  • Pythondaq: Docstring
  • Pythondaq: src-layout
  • Pythondaq: poetry
  • Pythondaq: test imports
  • Pythondaq: applicatie

Pythondaq: test imports

Bij het uitbouwen van de applicatie ga je mogelijk onderdelen uit de pythonpackage importeren. Daarom is het verstandig om, net als met de opdracht Packages, het importeren uit de package te testen. Maak daarvoor een tests-map met __init__.py en test_imports.py in de repository pythondaq.
test_imports.py
import pythondaq.view
Je runt het bestand test_imports.py en lost de errors op. Daarna werkt je package ook als je het aanroept van buiten de map met broncode. Je pythondaq-repository is nu een volledig project dat je met andere gebruikers van Python kunt delen, bijvoorbeeld via een wheel.
pythondaq
├──src
          └──pythondaq
                    ├──__init__.py
                    ├──arduino_device.py
                    ├──diode_experiment.py
                    └──view.py
├──tests
          ├──__init__.py
          └──test_imports.py
├──pyproject.toml
└──README.md

Pseudocode

view.py
# define from which package the module diode_experiment should be imported
...
Testcode
test_imports.py
import pythondaq.view
(ecpc) > python test_imports.py

Checkpunten:

  • Er is een map tests in de repository pythondaq.
  • Er is een bestand __init__.py in de map tests.
  • De import statements in de modules in het package pythondaq zijn aangepast zodat het bestand test_imports runt zonder problemen.

Projecttraject

  • Pythondaq: Docstring
  • Pythondaq: src-layout
  • Pythondaq: poetry
  • Pythondaq: test imports
  • Pythondaq: applicatie
Model, view, controller packages

In grotere projecten is het gebruikelijk om model, view, controller niet alleen uit te splitsen in verschillende scripts, maar ook in aparte packages te zetten.

  1. Maak 3 extra packages in de pythondaq package. models, views en controllers.
  2. Zet de modules in de juiste packages.
  3. Test je code zodat alle imports weer werken.

Van script naar applicatie

Om onze python code te testen heb je tot nu toe waarschijnlijk op de run-knop in Visual Studio Code gedrukt. Of je hebt in de terminal aan python gevraagd om het script.py te runnen:

Terminal
python script.py
Je moet dan wel de juiste map geopend hebben zodat python het bestand kan vinden. En als je de run-knop gebruikt moet wel het bestandje open hebben staan dat je wilt runnen. Kortom, best een beetje gedoe. Maar als we programma's zoals Poetry, Conda of Python willen gebruiken hoeven we helemaal niet het juiste bestandje op te zoeken en te runnen. We hoeven alleen maar een commando in de terminal te geven — bijvoorbeeld python of conda — en de computer start automatisch het juiste programma op.

Dat willen wij ook voor onze programma's! En omdat we Poetry gebruiken kunnen we dat heel eenvoudig doen. We gaan even in een andere test-repository een commando toevoegen om de module uit te voeren waarvan je de code in paragraaf Modules kunt vinden. De twee bestanden square.py en count_count.py hebben we voor jullie netjes in een package geplaats in de repository AnneliesVlaar/just_count met de volgende structuur:

just_count/
    src/
        just_count/
            __init__.py
            square.py
            count_count.py
    tests/
        __init__.py
    pyproject.toml
    README.md

De bestanden square.py en count_count.py zien er hetzelfde uit als in paragraaf Modules:

def square(x):
    return x**2


if __name__ == "__main__":
    print(f"The square of 4 is {square(4)}")
import square

print(f"The square of 5 is {square.square(5)}")

We kunnen Poetry niet vragen om een script te runnen, maar wel om een functie uit te voeren.

Main functie toevoegen

Je cloned de repository just_count in GitHub desktop en opent het daarna vanuit GitHub Desktop in Visual Studio Code. Je ziet een pyproject.toml in de repository staan. Dus installeer je het pakket met Poetry in een nieuwe conda environment (met alleen python=3.12) . Je opent het hoofdbestand count_count.py en zet de body van de module in een functie main(). Daarna pas je het bestand aan zodat de functie nog steeds wordt uitgevoerd wanneer je het bestand count_count.py runt.

Testcode

count_count.py
import square

def main():
    print(f"The square of 5 is {square.square(5)}")

if __name__ == '__main__':
    main()
(ecpc) > python count_count.py

Checkpunten:

  • Er is een functie main() in het bestand count_count.py
  • Het runnen van het bestand count_count.py geeft de output The square of 5 is 25

Projecttraject

  • main functie toevoegen
  • commando toevoegen
  • commando testen

In pyproject.toml kunnen we nu het commando toe gaan voegen. Met de scripts-tool van Poetry kunnen we aangeven met welk commando een functie uit een script wordt uitgevoerd. Om een commando toe te voegen ga je naar pyproject.toml en voeg je een extra kopje toe:

[tool.poetry.scripts]
naam_commando = "package.module:naam_functie"
Om de wijzigingen aan pyproject.toml door te voeren moet je de package opnieuw installeren.

commando toevoegen

Je voegt in de pyproject.toml het kopje [tool.poetry.scripts] toe. Je voegt vervolgens het commando square toe. Deze verwijst naar de functie main() welke in de module count_count.py staat die ondergebracht is in de package just_count. Omdat je handmatig het toml-bestand hebt aangepast installeer je het package opnieuw met Poetry .

Pseudo-code

pyproject.toml
[tool.poetry.scripts]
square = "just_count.count_count:main"

Checkpunten:

  • De naam van het commando is square.
  • De verwijzing na het = teken begint met twee aanhalingstekens gevolgd door het package just_count gevolgt door een punt.
  • Na de punt staat de naam van de module count_count.py zonder de extensie .py gevolgd door een dubbele punt.
  • Na de dubbele punt staat de naam van de functie main() zonder haakjes ().
  • Achter de functie staan weer dubble aanhalingstekens om de verwijzing te sluiten.
  • Na het opslaan van de pyproject.toml is het pakket opnieuw geïnstalleerd.

Projecttraject

  • main functie toevoegen
  • commando toevoegen
  • commando testen

Commando testen

Nu je het commando square hebt aangemaakt ga je deze testen in een terminal je ziet de tekst The square of 5 is 25 verschijnen. Je vraagt je af of het commando ook werkt als de terminal in een andere map zit. Met het commando cd.. ga je naar een bovenliggende map. Je test het commando square en ziet weer de tekst The square of 5 is 25 verschijnen. Je concludeert dat het commando nu overal werkt zolang het juiste conda environment is geactiveerd. Dat test je uit door een ander conda environment te activeren en het commando square nogmaal te proberen. Je krijgt een error en hebt daarmee je vermoeden bewezen. Tevreden ga je door naar de volgende opdracht.

Pseudo-code

(ecpc) > square 

Checkpunten:

  • Het commando square werkt als het juiste conda environment is geactiveerd.
  • Het commando square werkt nog steeds nadat je met het commando cd.. naar een bovenliggende map bent gegaan.
  • Het commando square werkt niet als een andere conda environment is geactiveerd.

Projecttraject

  • main functie toevoegen
  • commando toevoegen
  • commando testen
Error analysis

Als extra oefening gaan we met Poetry een commando maken om een ander script uit te laten voeren. De package is al aangemaakt, maar werkt nog niet naar behoren. Los in de volgende opdrachten de errors op om het script data_analysis.py te laten runnen.

  1. Ga naar GitHub en clone AnneliesVlaar/erroranalysis in GitHub Desktop en open de repository daarna in Visual Studio Code.
  2. Natuurlijk maak je gelijk een nieuwe Conda environment aan , voordat we dit package gaan testen.
  3. Snuffel door de bestanden en mappen, en open src/erroranalysis/data_analysis.py. Dit is het script wat moet kunnen runnen.
  4. Run het script data_analysis.py en los de errors één voor één op.

Om erachter te komen of de problemen die we hierboven hadden écht zijn opgelost maak je een nieuwe Conda environment aan , installeer je het package en run je het script. Werkt alles? Mooi! Dan gaan we nu een commando aanmaken om de functie table() aan te roepen.

  1. Open pyproject.toml en voeg een kopje toe voor scripts.
    [tool.poetry.scripts]
    naam_commando = "package.module:naam_functie"
    
    pas de regel aan zodat jouw commando de functie table() aanroept in src/erroranalysis/data_analysis.py. Je mag de naam van het commando zelf kiezen.
  2. Ga naar de terminal en kijk of het werkt!
    (ecpc) > naam_commando 
    
    

Pythondaq: applicatie

Je maakt een commando om het script view.py uit de repository pythondaq te starten . Wanneer je het commando aanroept gaat het LED-lampje branden, en verschijnt er even later een IU-plot op het scherm. Je test of het commando ook buiten Visual Studio Code werkt door een Anaconda prompt te openen. Je activeert het juiste conda environment en ziet dat ook dan het commando werkt. Wat een feest! Je hebt nu een applicatie geschreven die een Arduino aanstuurt om een ledje te laten branden. En je kunt je applicatie gewoon vanuit de terminal aanroepen!

Pseudo-code

view.py
# import statements

# def function
    # code to start a measurement
pyproject.toml
[tool.poetry.scripts]
naam_commando = "package.module:naam_functie"

Checkpunten:

  • De functie in view.py bevat alle code die uitgevoerd moet worden om een meting te starten.
  • Het commando in de pyproject.toml verwijst op de correcte manier naar de functie in view.py.
  • Het aanroepen van het commando zorgt ervoor dat een meting gestart wordt.
  • Het commando werkt ook in een Anaconda prompt zolang het juiste conda environment actief is.

Projecttraject

  • Pythondaq: Docstring
  • Pythondaq: src-layout
  • Pythondaq: poetry
  • Pythondaq: test imports
  • Pythondaq: applicatie
Versie 2.0.0

In de pyproject.toml kan je ook de versie aangeven van je package. Maar wanneer hoog je nu welk cijfertje op? Wanneer wordt iets versie 2.0.0? Daar zijn conventies voor. Bug fixes gaan op het laatste cijfer, wijzigingen en nieuwe features gaan op het middelste cijfer. Wanneer de applicatie dusdanig verandert dat je bijvoorbeeld bestanden die je met oude versie hebt gemaakt niet met de nieuwe versie kunt openen, dan verander je het eerste cijfer. Je start vaak met versie 0.1.0 en blijft tijdens het bouwen van je project ophogen naar 0.2.0 en soms zelfs 0.83.0. Wanneer je project min of meer klaar is voor eerste gebruik, dan kies je er vaak voor om versie 1.0.0 te releasen.


  1. Echt gebeurd: meerdere studenten leverden hun grafische applicatie in voor een beoordeling. We konden het niet draaien, want er misten bestanden. Bij de student werkte het wel, maar bij ons echt niet. 

  2. Wanneer de repository op GitHub wordt geplaatst wordt deze README automatisch op de hoofdpagina van de repository getoond, onder de code. 

  3. Python heeft een ingebouwde module unittest die deze tests kan vinden, kan runnen en daarna een handige weergave geeft van welke tests geslaagd zijn en welke faalden. Ook het package pytest is erg bekend. Op deze manier weet je altijd zeker dat wanneer je aanpassingen doet in je code, dat de rest van de code nog steeds is blijven werken — zónder dat je zelf uitvoerig alles hebt hoeven uitproberen. Je draait gewoon even snel alle tests. Helaas, helaas — in deze cursus is te weinig tijd om het schrijven van tests te behandelen. 

  4. Ja er is een map easystat met daarin een map src met daarin weer een map easystat — dat kan nog wel eens verwarrend zijn. Het is conventie om de projectmap dezelfde naam te geven als je package. Het pad is dus eigenlijk project/src/package en dat wordt dan, in ons geval, easystat/src/easystat

  5. Vroeger was er een setup.py maar Python schakelt nu langzaam over naar dit nieuwe bestand. 

  6. In f-strings kunnen tussen de accolades variabelen of functieaanroepen staan. Voeg daar het =-teken aan toe en je krijgt niet alleen de waarde, maar ook de variabele of aanroep zelf te zien. Bijvoorbeeld: als je definieert name = "Alice", dan geeft print(f"{name}") als uitkomst Alice. Maar voeg je het =-teken toe zoals in print(f"{name=")} wordt de uitvoer name='Alice'. Je ziet dan dus ook meteen de naam van de variabele en dat kan handig zijn. 

  7. Dit is bij het aanmaken standaard ingevuld op basis van de Python versie die in de base environment zit, kijk maar met conda list in de base environment welke versie van Python daarin zit. 

  8. Het is eenvoudig om zelf de pyproject.toml te openen en daar wat in aan te passen voor zover nodig. 

  9. PySerial is een package die we gebruiken om te communiceren over USB poorten. 

  10. Hynek Schlawack. Testing & packaging. URL: https://hynek.me/articles/testing-packaging/

  11. Tom Preston-Werner, Pradyun Gedam, and others. Tom's obvious, minimal language. URL: https://github.com/toml-lang/toml