Command-line interface¶
Gebruikersomgevingen¶
Vanaf de jaren '60 van de vorige eeuw werden computers interactief. Het was mogelijk om via een terminal commando's aan de computer te geven en te wachten op een antwoord. In tegenstelling tot moderne gebruikersomgevingen waren deze volledig op tekst gebaseerd. Hoewel moderne besturingssystemen — of het nu computers, tablets of mobiele telefoons betreft — volledig grafisch zijn ingericht, is de tekstuele interface nooit verdwenen. Opdrachten geven door te typen is gewoon best wel handig en snel. Ook is het veel eenvoudiger om applicaties te ontwikkelen zonder grafische interface.
Op ieder besturingssysteem — Linux, MacOS, Windows — is een shell, terminal of command prompt te vinden. Als je die opstart kun je op de zogeheten command line opdrachten intypen. Veelal zijn dit commando's om door het bestandssysteem te navigeren en programma's op te starten.
Wanneer je in Visual Studio Code een Pythonscript start dan opent het een terminal onderin het scherm.
Commando's¶
Je hebt tot nu toe al heel wat commando's in de terminal getypt. Laten we een paar voorbeelden bestuderen:
Als eerste vertel je welke applicatie je wilt gaan starten; in dit geval:python
. Daarna geef je met het argument script.py
aan welk Pythonscript je wilt uitvoeren. Vaak kun je ook opties1 meegeven zoals in:
PS> python -V
Python 3.13.5
Hiermee vraag je Python om het versienummer weer te geven. Soms kunnen opties zelf weer een argument meekrijgen. Bijvoorbeeld:
Met deze regel geef je Python de optie-m
mee en die importeert een module (hier antigravity
) en voert die uit. Probeer maar eens zelf wat er gebeurt als je dat commando uitvoert.
Als applicaties veel verschillende functionaliteit hebben dan krijg je regelmatig te maken met een lange regel met een combinatie van argumenten en opties:
Uitgesplitst in argumenten en opties, met vierkante haken [] om aan te geven welke onderdelen bij elkaar horen, is dat:uv venv [--python 3.13]
uv argumenten
- Naast
uv venv
heb je ook met andere argumenten gewerkt zoalssync
enadd
. Welke argumenten ken je al van de applicatieuv
? - Vraag de lijst met argumenten (commando's) op van uv met
uv help
(scroll terug naar boven om alles te zien). Hoeveel kende je nog niet?
Projecttraject
- uv argumenten
- uv opties en argumenten
uv opties en argumenten
Het gaat er bij deze opdracht om dat je een beetje bekend raakt met helpteksten, het verschil tussen argumenten en opties en waar je informatie kunt vinden. Je hoeft daarvoor niet alle helpteksten volledig door te lezen of alle opties te bekijken. Dat is namelijk heel veel.
- Open een terminal en bekijk de helptekst van uv (
uv help
). - Zoek de optie op om de uv versie weer te geven. Wat is deze versie?
- Maak gebruik van het argument
help
om de helpfunctie van het commando pip op te vragen (uv help pip
) - Welke argumenten moet je meegeven (positional arguments?) en welke opties mag je meegeven (optional arguments?) (argument:
COMMAND
, opties o.a. :--no-cache
,--managed-python
)?
Projecttraject
- uv argumenten
- uv opties en argumenten
Click¶
Als je gebruik wilt maken van commando's in jouw eigen applicatie dan moet je weten wat de gebruiker in de terminal typt. Dit is mogelijk met sys.argv
.2 Hiermee wordt alles wat in de terminal getypt wordt aan input meegegeven:
Met if-statements kun je acties verbinden aan bepaalde argumenten:
import sys
args = sys.argv
print(args)
if args[1] == "test":
print(f"This is a test: {args[2]}")
else:
print(f"CommandNotFoundError: No command '{args[1]}'.")
Als je meerdere opties en argumenten meegeeft dan wordt het veel werk om die in je script uit elkaar te plukken en ze goed te interpreteren. Om dat makkelijker te maken zijn er verschillende bibliotheken beschikbaar — waaronder een paar in de standard library van Python. Een hele handige — die níet in de standard library van Python zit, maar wél heel populair is — is Click.6
Info
Click maakt gebruik van decorators (@decorator
). Om decorators te gebruiken, hoef je niet per se te weten hoe ze werken. Als je meer wilt weten over de werking ervan kijk dan de calmcode tutorial over decorators. Of lees het meer-leren blok over decorators of de Primer on Python Decorators.
Als kort voorbeeld — geïnspireerd op de documentatie van Click — neem je het volgende script:
Dit script print de uitdrukking "Hello physicist!". Je gaat dit script aanpassen en maakt het daarmee mogelijk om de naam en het aantal begroetingen te kiezen. Hiervoor gebruik je Click. Allereerst moet je click
importeren en aangeven dat je de hello()
-functie wilt gebruiken als commando:
import click
@click.command()
def hello():
print("Hello physicist!")
if __name__ == "__main__":
hello()
Dit levert je nog niet zoveel op, maar op de achtergrond is Click wel degelijk aan het werk. De @click.command()
houdt in de gaten wat er in de command-line wordt ingetypt. Zo kun je de helpfunctie aanroepen door --help
achter de naam van het script te zetten:
Om de code te runnen moet je wel een virtual environment aanmaken, activeren en click
installeren.
Helpfunctie
hello.py
over. Je vraagt de helpfunctie van het script op. Je ziet een helptekst verschijnen. Je vraagt je af wat er gebeurt als je @click.command()
weghaalt en daarna de helpfunctie opvraagt. Je krijgt gewoon de output van de functie hello()
en geen helptekst.
ECPC
├──
oefenopdrachten
├──
hello.py
└── •••
├──
pythondaq
└── •••
Pseudo-code
import click
# make function Click command
# function
# print hello physicist!
# when run this script:
# run function
(oefenopdrachten) > python hello.py --help
Usage: hello.py [OPTIONS]
Options:
--help Show this message and exit.
Checkpunten
- Je vraagt de helpfunctie op door
--help
achter de bestandsnaam te zetten in de terminal. - Er verschijnt een standaard helptekst.
- Zonder
@click.command()
verschijnt er geen helptekst, maar de output van de functie.
Projecttraject
- Helpfunctie
- Argument toevoegen
- Optie toevoegen
- Helptekst toevoegen
- Extra optie toevoegen
- Vlag toevoegen
In de code hieronder wordt met de regel @click.argument("name")
aangegeven dat je van de gebruiker een argument verwacht. Zorg dat het argument ook gebruikt wordt in de functie hello
:
import click
@click.command()
@click.argument("name")
def hello(name):
print(f"Hello {name}!")
if __name__ == "__main__":
hello()
Argument toevoegen
Je runt het bestand hello.py
en geeft achter de bestandsnaam de naam Alice
mee. Er verschijnt Hello Alice!
als output in de terminal.
Pseudo-code
import click
# make function Click command
# make argument name
# function, parameter name
# print hello <name>!
# when run this script:
# run function
(oefenopdrachten) > python hello.py
Usage: hello.py [OPTIONS] NAME
Try 'hello.py --help' for help.
Error: Missing argument 'NAME'.
Checkpunten
- Het draaien van
hello.py
zonder een argument (python hello.py
) geeft een foutmelding. - Het draaien van
hello.py
met een argument (python hello.py Alice
) werkt zoals verwacht.
Projecttraject
- Helpfunctie
- Argument toevoegen
- Optie toevoegen
- Helptekst toevoegen
- Extra optie toevoegen
- Vlag toevoegen
Warning
Let er op dat je bij @click.argument
de naam meegeeft die overeenkomt met de namen van de parameters van je functie. In dit geval heb je een argument "name"
, dit moet overeenkomen met de functiedefinitie def hello(name):
.
Argumenten zijn altijd verplicht en moeten in een vaste volgorde staan. Bij opties is dat anders. Je geeft met mintekens aan dat je een optie meegeeft. Veel opties hebben een lange naam en een afkorting (bijvoorbeeld --count
en -c
). Opties kunnen zelf weer een argument hebben (bijvoorbeeld --count 3
). Het is handig om een standaardwaarde te definiëren. In dat geval mag de gebruiker de optie weglaten. Je voegt een for-loop toe om de begroeting te herhalen.3
import click
@click.command()
@click.argument("name")
@click.option(
"-c",
"--count",
default=1,
)
def hello(name, count):
for _ in range(count):
print(f"Hello {name}!")
if __name__ == "__main__":
hello()
Optie toevoegen
Je runt het bestand hello.py
en geeft achter de bestandsnaam de naam van je assistent mee. Tevens geef je aan dat je de begroeting vijf keer wilt herhalen. Er verschijnt vijf keer Hello <assistent>!
als output in de terminal.
Pseudo-code
import click
# make function Click command
# make argument name
# make option count with default value 1
# function, parameter name and count
# repeat count times
# print hello <name>!
# when run this script:
# run function
(oefenopdrachten) > python hello.py David -c 5
Hello David!
Hello David!
Hello David!
Hello David!
Hello David!
Checkpunten
- Je kan de naam van je assistent 5 keer printen met één commando.
- Je kan het aantal keer printen opgeven met
-c
. - Je kan het aantal keer printen ook opgeven met
--count
. - Wanneer de optie
count
wordt weggelaten wordt de naam één keer geprint. -
Wanneer er geen argument wordt meegegeven met de optie
count
volgt een foutmelding:(oefenopdrachten) > python hello.py David -c Error: Option '-c' requires an argument.
Projecttraject
- Helpfunctie
- Argument toevoegen
- Optie toevoegen
- Helptekst toevoegen
- Extra optie toevoegen
- Vlag toevoegen
Warning
Let er op dat je bij @click.option
de afkorting met één minteken meegeeft en de lange naam met twee mintekens. De lange naam moet overeenkomen met de paramater van je functie. In ons geval hebben we een optie "--count"
— de lange naam telt. Dit moet overeenkomen met de functiedefinitie def hello(name, count):
.
Het is handig om een korte helptekst toe te voegen. Dit gaat als volgt:
import click
@click.command()
@click.argument("name")
@click.option(
"-c",
"--count",
default=1,
help="Number of times to print greeting.",
show_default=True, # show default in help
)
def hello(name,count):
for _ in range(count):
print(f"Hello {name}!")
if __name__ == "__main__":
hello()
Helptekst toevoegen
Voeg de helptekst toe. Vraag daarna de helptekst op zoals in de opdracht Helpfunctie.
Pseudo-code
Projecttraject
- Helpfunctie
- Argument toevoegen
- Optie toevoegen
- Helptekst toevoegen
- Extra optie toevoegen
- Vlag toevoegen
Als je bovenstaand script gebruikt ziet dat er zo uit:
(oefenopdrachten) > python hello.py --help
Usage: hello.py [OPTIONS] NAME
Options:
-c, --count INTEGER Number of times to print greeting. [default: 1]
--help Show this message and exit.
(oefenopdrachten) > python hello.py Alice
Hello Alice!
(oefenopdrachten) > python hello.py Alice -c 2
Hello Alice!
Hello Alice!
(oefenopdrachten) > python hello.py Alice --count 3
Hello Alice!
Hello Alice!
Hello Alice!
Extra optie toevoegen
Je runt het bestand hello.py
en geeft achter de bestandsnaam de naam van je assistent mee. Tevens geef je aan dat je de begroeting vijf keer wilt herhalen met een pauze van twee seconde ertussen. Het duurt dus acht seconden voordat er vijf keer Hello <assistent>!
als output in de terminal staat. Als je de optie voor een pauze niet meegeeft, wordt er ook geen pauze gehouden.
Info
Je kan gebruikmaken van de module time die standaard met Python meekomt.4 Met de functie sleep()
kun je de executie van de volgende regel in het script met een aantal seconden uitstellen.
Pseudo-code
import click
# make function Click command
# make argument name
# make option count with default value 1 and help
# make option pause
# function, parameter name, count and pause
# repeat count times
# print hello <name>!
# pause
# when run this script:
# run function
(oefenopdrachten) > python hello.py David -c 5
Hello David!
Hello David!
Hello David!
Hello David!
Hello David!
Checkpunten
- Als de optie voor pauze niet wordt meegegeven, dan wordt er géén pauze ingelast.
- Bij het meegeven van de optie voor pauze, wacht het programma zo lang als verwacht.
Projecttraject
- Help functie
- Argument toevoegen
- Optie toevoegen
- Helptekst toevoegen
- Extra optie toevoegen
- Vlag toevoegen
Opties zonder argument werken als vlag — een soort aan-/uitknop.5
Vlag toevoegen
Gebruik een optie als vlag om de gebruiker te laten kiezen tussen het wel (tea
) of niet (no tea
) aanbieden van een kopje thee. Zorg dat er standaard tea
wordt aangeboden.
Boolean flags
Lees meer over boolean flags in de Click documentatie.
Pseudo-code
import click
# make function Click command
# make argument name
# make option count with default value 1 and help
# make option pause
# make boolean flag tea/no-tea
# function, parameter name, count, pause and tea
# repeat count times
# print hello <name>!
# if wished offer tea
# pause
# when run this script:
# run function
(oefenopdrachten) > python hello.py David -c 5
Hello David! Here is your tea.
Hello David! Here is your tea.
Hello David! Here is your tea.
Hello David! Here is your tea.
Hello David! Here is your tea.
Checkpunten
- Als de vlag voor een kopje thee niet wordt meegegeven, dan wordt er een kopje thee aangeboden.
- Als de vlag
tea
wordt meegegeven, dan wordt er ook een kopje thee aangeboden. - Als de vlag
no-tea
wordt meegegeven, dan wordt er geen kopje thee aangeboden.
Projecttraject
- Help functie
- Argument toevoegen
- Optie toevoegen
- Helptekst toevoegen
- Extra optie toevoegen
- Vlag toevoegen
Easystat: click
Je opent met Github Desktop de repository easystat
in Visual Studio Code. Je hebt eerder een virtual environment voor deze repository aangemaakt, maar je hebt geen idee of die in de tussentijd per ongeluk stuk is gegaan. Daarom synchroniseer je de virtual environment . En je test of je de applicatie nog kunt aanroepen met het commando easystat
.
Je past de code in het bestand app.py
zo aan dat met het commando easystat 4 5 6
het resultaat van de serie metingen met waardes 4, 5 en 6 in de terminal wordt geprint.
Info
Click maakt van alle argumenten een string, tenzij je een default waarde of een type definieert. Gebruik type=int
, type=float
enzovoorts om aan te geven wat voor type object het argument moet worden. Gebruik daarnaast nargs=-1
om aan te geven dat je argument meerdere waardes accepteert, en zelfs oneindig veel (-1).
Pseudo-code
from easystat.measurements import result_with_uncertainty
# add click-related code to pass measurements as an argument to easystat
def main(measurements):
result, uncertainty = result_with_uncertainty(measurements)
print(f"{measurements=}")
print(f"Result of measurements is: {result:.2f} +- {uncertainty:.2f}.")
if __name__ == "__main__":
main()
(oefenopdrachten) > easystat 4 5 6
measurements=(4.0, 5.0, 6.0)
Result of measurements is: 5.00 +- 0.47.
Checkpunten
- Je hebt de virtual environment gesynchroniseerd met
uv sync
. - Je hebt de juiste virtual environment geactiveerd.
- Je hebt click als dependency toegevoegd aan
pyproject.toml
. - Je hebt in het bestand
app.py
code toegevoegd om meerdere metingen als argument mee te kunnen geven. - Na het commando
easystat
kun je getallen meegeven en krijg je het verwachte antwoord terug.
Projecttraject
- Easystat: click
Easystat: verplicht argument
Pas de applicatie zo aan dat je zónder argument — dus zonder metingen — geen gekke uitkomst krijgt met nan +- nan
, maar de uitleg dat je een argument moet meegeven. Met andere woorden: je argument is required.
Click subcommando's¶
Tot nu toe kon je maar één functie uitvoeren in jouw applicatie. Maar het is ook mogelijk om subcommando's aan te maken, zodat je met één programma meerdere taken
kunt uitvoeren. Denk bijvoorbeeld aan uv
: je voegt packages toe aan je project met uv add
, verwijdert ze met uv remove
, maakt een virtual environment met uv venv
en synchroniseert het met uv sync
.
Pythondaq: subcommando's bedenken
Je gaat de applicatie pythondaq
straks verder uitbreiden, zodat er veel meer mogelijk is dan nu. Wat zou je willen dat de applicatie allemaal kan? Welke subcommando's wil je gaan aanmaken? Overleg met elkaar om goede ideeën uit te wisselen.
Projecttraject
- Pythondaq: subcommando's bedenken
- Pythondaq: commando's
- Pythondaq:
scan
- Pythondaq: herhaalmetingen
- Pythondaq:
list
- Pythondaq:
info
- Pythondaq: choose device
- Pythondaq: grafiek
- Pythondaq:
--help
- Pythondaq: alle subcommando's geïmplementeerd?
Een eenvoudig voorbeeldscript waarin de uv commando's add
en remove
worden nagebootst leggen we hieronder uit. Eerst de code:
cmd_group()
genoemd is en die redelijk bovenaan gedefinieerd wordt. In tegenstelling tot het hello.py
-script doet deze functie helemaal niets (pass
). Je vertelt aan click dat je een groep van commando's aan gaat maken met de @click.group()
-decorator in regel 4. Vervolgens ga je commando's binnen deze groep hangen door niet de decorator @click.command()
te gebruiken, maar @cmd_group.command()
— zie regels 9 en 15. De namen van de commando's die worden aangemaakt zijn de namen van de functies. Dus regel 9 en 11 maken samen het commando add
. Verder werkt alles hetzelfde. Dus een argument toevoegen — zoals in regel 10 — is gewoon met @click.argument()
. Hier hoef je geen cmd_group
te gebruiken.
Fake uv
ECPC
een nieuw uv project aan voor fake_uv
. In deze map zet je de code uit het bestand fake_uv.py
. Je maakt een nieuw virtual environment aan met daarin de benodigde packages . Je past de pyproject.toml
aan zodat je met het commando fake_uv add scipy
zogenaamd scipy
kunt installeren .
ECPC
├──
fake_uv
├──
src/fake_uv
├──
__init__.py
└──
fake_uv.py
└──
pyproject.toml
└── •••
├──
oefenopdrachten
├──
pythondaq
└── •••
Commando
Als je een commando met uv toevoegt dan heeft dat de opbouw naam_commando = "package.module:naam_functie"
. Welke functie moet uitgevoerd worden als je het commando aanroept?
Pseudo-code
Testcode(oefenopdrachten) > fake_uv add scipy
Adding and installing scipy...
Checkpunten
- Je hebt een uv project met een actief virtual environment.
- Na het wijzigen van de
pyproject.toml
is het virtual environment opnieuw gesynchroniseerd. - In de
pyproject.toml
verwijst[project.scripts]
naar een functie zodatadd
enremove
subcommando's zijn. - Het commando
fake_uv add scipy
print de tekstAdding and installing scipy...
als output in de terminal.
Projecttraject
- Fake uv
Smallangle: project aanmaken
Met deze opdracht kun je testen hoe goed je het Python-jargon onder de knie hebt. Je zult het woord smallangle
zó vaak tegenkomen dat het je duizelt — maar jij weet precies over welk onderdeel het gaat.
- Maak een nieuw uv project aan met de naam
smallangle
. Let op de Octocat voorsmallangle
, het moet dus een repository zijn (of worden).
ECPC
├──pythondaq
└──smallangle
└── •••
└── ••• - Zet in het package
smallangle
een modulesmallangle.py
. - Plak onderstaande code in
smallangle.py
:import numpy as np from numpy import pi import pandas as pd def sin(number): x = np.linspace(0, 2 * pi, number) df = pd.DataFrame({"x": x, "sin (x)": np.sin(x)}) print(df) def tan(number): x = np.linspace(0, 2 * pi, number) df = pd.DataFrame({"x": x, "tan (x)": np.tan(x)}) print(df) if __name__ == "__main__": sin(10)
- Run het bestand
smallangle.py
en los de foutmeldingen op door de juiste dependencies toe te voegen aan het bestandpyproject.toml
. Als je alle foutmeldingen opgelost hebt krijg je een lijst van tien punten tussen 0 en 2$\pi$ en de sinus van deze punten.
Testcode
import numpy as np
from numpy import pi
import pandas as pd
def sin(number):
x = np.linspace(0, 2 * pi, number)
df = pd.DataFrame({"x": x, "sin (x)": np.sin(x)})
print(df)
def tan(number):
x = np.linspace(0, 2 * pi, number)
df = pd.DataFrame({"x": x, "tan (x)": np.tan(x)})
print(df)
if __name__ == "__main__":
sin(10)
(smallangle) > python smallangle.py
x sin (x)
0 0.000000 0.000000e+00
1 0.698132 6.427876e-01
2 1.396263 9.848078e-01
3 2.094395 8.660254e-01
4 2.792527 3.420201e-01
5 3.490659 -3.420201e-01
6 4.188790 -8.660254e-01
7 4.886922 -9.848078e-01
8 5.585054 -6.427876e-01
9 6.283185 -2.449294e-16
Checkpunten
- De module
smallangle.py
staat in de mapsrc/smallangle
. - Alle benodigde dependencies zijn toegevoegd aan het bestand
pyproject.toml
. - Het runnen van het bestand
smallangle.py
geeft een lijst van tien punten tussen 0 en 2$\pi$ en de sinus van deze punten.
Projecttraject
- Smallangle: project aanmaken
- Smallangle: subcommando's en opties
- Smallangle: docstrings
Smallangle: subcommando's en opties
Je kunt met het commando smallangle
en de subcommando's sin
en tan
een lijst genereren van getallen tussen de 0 en 2$\pi$ en de bijbehorende sinus dan wel tangens van deze getallen. Met de optie -n
kun je het aantal stappen — het aantal $x$-waardes tussen 0 en $2\pi$ — kiezen. Als je de optie -n
weglaat werkt de applicatie met een standaardwaarde.
TypeError: 'int' object is not iterable
Krijg je onderstaande foutmelding tijdens het draaien van het bestand smallangle
?
Traceback (most recent call last):
File "c:\smallangle\src\smallangle\smallangle.py", line 28, in <module>
sin(10)
File "C:\click\core.py", line 1157, in __call__
return self.main(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\click\core.py", line 1067, in main
args = list(args)
^^^^^^^^^^
TypeError: 'int' object is not iterable
Dan komt dat doordat je sin(10)
probeert uit te voeren, terwijl de functie al verClickt is. De functie verwacht een argument vanuit de terminal en geen integer vanuit het pythonscript.
Pas je script aan zodat if __name__ == "__main__":
naar de juiste functie verwijst en Click aanroept; niet sin(10)
.
(smallangle) > smallangle sin -n 9
x sin (x)
0 0.000000 0.000000e+00
1 0.785398 7.071068e-01
2 1.570796 1.000000e+00
3 2.356194 7.071068e-01
4 3.141593 1.224647e-16
5 3.926991 -7.071068e-01
6 4.712389 -1.000000e+00
7 5.497787 -7.071068e-01
8 6.283185 -2.449294e-16
Checkpunten:
- De gebruiker kan met het commando
smallangle
het scriptsmallangle.py
starten. - De gebruiker kan met subcommando's kiezen tussen
sin
entan
. Let op: het gaat hier om subcommando's! - De gebruiker kan het aantal stappen kiezen met een optie.
- De gebruiker kan de optie ook weglaten.
Projecttraject:
- Smallangle: project aanmaken
- Smallangle: subcommando's en opties
- Smallangle: docstrings
Smallangle: kleine hoekbenadering
Met het commando approx
en een argument $\epsilon$ moet het script de grootste hoek geven waarvoor nog geldt dat $\lvert x - \sin(x) \rvert \leq \epsilon$, ofwel de grootste hoek waarvoor de kleine hoekbenadering nog geldt met de opgegeven nauwkeurigheid. Doe dit op drie cijfers nauwkeurig (loop over .000, .001 en .002, etc. totdat de vergelijking niet meer geldt). Belangrijk: besteed geen tijd aan het analytisch oplossen van de vergelijking. Een voorbeeld van de uitvoer:
(smallangle) > smallangle approx .1
For an accuracy of 0.1, the small-angle approximation holds
up to x = 0.854.
Docstrings en Click --help
¶
Docstrings werken ook heel handig samen met Click. Ze worden gebruikt als je de helpfunctie aanroept.
Info
We gebruiken bij Click-functies niet de standaard structuur voor docstrings. Click breekt docstrings namelijk standaard af waardoor het algauw een onmogelijke brij aan informatie wordt. We kiezen daarom voor een samenvatting in een zin met daarin de PARAMETERS (argumenten) in hoofdletters en eventueel een korte toelichting daarop.
Uitgebreide documentatie en Click
In de documentatie van Click vind je meer informatie over het afbreken van zinnen (en het voorkomen daarvan). Ook vind je daar een manier om een uitgebreide docstring te schrijven zonder dat het een bende wordt.
Tijd om docstrings toe te voegen aan fake uv:
"""A fake implementation of several uv commands."""
import click
@click.group()
def cmd_group():
"""Fake uv commands."""
pass
@cmd_group.command()
@click.argument("package")
def add(package):
"""Add a package to a uv project.
PACKAGE is the package to add to the project.
"""
print(f"Adding and installing {package}...")
@cmd_group.command()
@click.argument("package")
def remove(package):
"""Remove a package from a uv project.
PACKAGE is the package to remove.
"""
print(f"Removing {package}...")
if __name__ == "__main__":
cmd_group()
(oefenopdrachten) > fake_uv --help
Usage: fake_uv.py [OPTIONS] COMMAND [ARGS]...
Fake uv commands.
Options:
--help Show this message and exit.
Commands:
add Add a package to a uv project.
remove Remove a package from a uv project.
Daarna kun je uitleg vragen over de subcommando's, waarbij je de hele docstring te zien krijgt:
(oefenopdrachten) > fake_uv add --help
Usage: fake_uv.py add [OPTIONS] PACKAGE
Add a package to a uv project.
PACKAGE is the package to add to the project.
Options:
--help Show this message and exit.
Smallangle: docstrings
Je gebruikt het commando smallangle --help
en leest de helptekst van de opdracht smallangle. De helptekst bevat zinvolle informatie die je in staat stelt om te begrijpen wat je met de applicatie kan doen. Je ziet dat er twee subcommando's zijn en bekijkt de helptekst van deze subcommando's met smallangle sin --help
en daarna smallangle tan --help
. Beide helpteksten stellen je in staat om de applicatie te begrijpen en te bedienen.
Pseudo-code
Testcode(smallangle) > smallangle --help
Usage: smallangle [OPTIONS] COMMAND [ARGS] ...
Options:
--help Show this message and exit.
Commands:
Subcommand Summary containing ARGUMENT(S).
Checkpunten
-
smallangle --help
geeft zinvolle informatie. -
smallangle sin --help
geeft zinvolle informatie. -
smallangle tan --help
geeft zinvolle informatie.
Projecttraject
- Smallangle: project aanmaken
- Smallangle: subcommando's en opties
- Smallangle: docstrings
Command-line interface voor ons experiment¶
In hoofdstuk Model-View-Controller heb je pythondaq
uitgesplitst in model, view en controller. Wanneer we een command-line interface gaan bouwen dan is dat de softwarelaag tussen de gebruiker en de rest van de code. De command-line interface is dus een view. Het is helemaal niet gek om meerdere views te hebben, bijvoorbeeld een eenvoudig script zoals run_experiment.py
, een command-line interface en een grafische interface. Hier gaan we ons richten op een command-line interface. We gaan een nieuw bestand cli.py
aanmaken en dat langzaam opbouwen.
Pythondaq: commando's
src/pythondaq/cli.py
een opzetje maken waar je stap voor stap functionaliteit aan toevoegt. De oude run_experiment.py
maakte eerst een lijst van aangesloten apparaten en daarna werd een scan uitgevoerd. Daarom zet je in cli.py
de subcommando's list
en scan
. En zorg je dat ze voor nu alleen een stukje tekst printen.
De gebruiker test de test-subcommmando's met de volgende handelingen. De gebruiker typt in de terminal het commando diode
met daarachter het subcommando list
en ziet een tekst verschijnen: Work in progress, list devices
. De gebruiker test vervolgens het subcommando scan
en ziet de tekst Work in progress, scan LED
verschijnen.
ECPC
├──
pythondaq
├──
src/pythondaq
├──
__init__.py
├──
arduino_device.py
├──
diode_experiment.py
├──
run_experiment.py
└──
cli.py
└── •••
└── •••
Warning
Omdat de naam van een subcommando gelijk is aan de functienaam kan dat voor problemen zorgen wanneer je gereserveerde namen van python wilt gebruiken zoals: import
, return
, lambda
. Of wanneer je de naam van het subcommando graag hetzelfde wilt hebben als een ander pythonfunctie zoals sin
of list
.
Een oplossing is om de functienaam aan te passen en de subcommando naam expliciet aan click mee te geven bij command
:
@cmd_group.command("import")
@click.argument("package")
def import_package(package):
print(f"import {package}...")
import
aangemaakt — niet een commando import_package
.
Pseudo-code
# subcommando list
# print Work in progress, list devices
# subcommando scan
# print Work in progress, scan LED
(oefenopdrachten) > diode list
Work in progress, list devices
(oefenopdrachten) > diode scan
Work in progress, scan LED
Checkpunten:
- De applicatie is aan te roepen met
diode
. - Het subcommando
list
print een stukje tekst. - Het subcommando
scan
print een ander stukje tekst.
Projecttraject:
- Pythondaq: commando's
- Pythondaq:
scan
- Pythondaq: herhaalmetingen
- Pythondaq:
list
- Pythondaq:
info
- Pythondaq: choose device
- Pythondaq: Grafiek
- Pythondaq:
--help
Het uitvoeren van een meetserie
¶
We gaan ons eerst richten op het uitvoeren van een volledige meetserie en het tonen van de resultaten daarvan aan de gebruiker.
Info
Bij het opgeven van argumenten en opties voor de spanning kan het belangrijk zijn om te controleren of de spanning überhaupt wel een getal is tussen 0 en 3.3 V. Je kunt dit doen door de type
-parameter in @click.argument()
en @click.option()
. Je kunt een Pythontype opgeven (bijvoorbeeld: type=int
of type=float
) en Click heeft speciale types zoals type=click.FloatRange(0, 3.3)
voor een kommagetal tussen 0 en 3.3. Bekijk alle speciale types in de Click documentatie. Als je hiervan gebruik maakt hoef je niet zelf te controleren of de parameters kloppen. Click doet dat voor je.
Pythondaq: scan
Pas het subcommando scan
aan.
De gebruiker test het subcommando scan
met de volgende handelingen. De gebruiker typt het commando diode scan
in de terminal. Aan de hand van de helptekst weet de gebruiker dat het met argumenten of opties mogelijk is om het spanningsbereik (in Volt) aan te passen. Er wordt een meting gestart die binnen het spanningsbereik blijft. De gebruiker ziet dat de stroomsterkte dóór en de spanning óver de LED in de terminal worden geprint. De gebruiker start een nieuwe meting en geeft ditmaal met de optie --output FILENAME
een naam voor een CSV-bestand mee. Dit keer worden de metingen ook opgeslagen als CSV-bestand onder de meegegeven bestandsnaam.
Pseudo-code
Checkpunten:
- Het programma print een lijst van metingen van de stroomsterkte dóór en de spanning óver de LED.
- De gebruiker moet het spanningsbereik (in volt) zelf kunnen opgeven met argumenten of opties.
- De gebruiker kan de metingen opslaan in een CSV-bestand met een optie
--output FILENAME
. - De meting wordt alleen opgeslagen als de optie wordt meegegeven.
Projecttraject:
- Pythondaq: commando's
- Pythondaq:
scan
- Pythondaq: herhaalmetingen
- Pythondaq:
list
- Pythondaq:
info
- Pythondaq: choose device
- Pythondaq: Grafiek
- Pythondaq:
--help
Pythondaq: herhaalmetingen
Pas het subcommando scan
aan zodat je met een optie het aantal herhaalmetingen kan kiezen.
De gebruiker test de optie om het aantal herhaalmetingen te kiezen met de volgende handelingen. Met het subcommando scan
voert de gebruiker een meting uit in het bereik 2.8V tot 3.3V. Met een optie zet de gebruiker het aantal herhaalmetingen op 5. De gebruiker ziet dat het resultaat van de metingen met onzekerheden worden geprint in de terminal. De gebruiker bekijkt de grootte van de onzekerheden en voert nogmaals een scan uit maar dan met 10 metingen en daarna met 20 metingen. De gebruiker ziet dat de onzekerheden afnemen wanneer het aantal metingen toeneemt.
Pseudo-code
Checkpunten:
- De gebruiker kan het aantal herhaalmetingen met een optie kiezen.
- De herhaalmetingen worden gebruikt om de beste schatting en onzekerheid te berekenen van de stroomsterkte en de spanning.
Projecttraject:
- Pythondaq: commando's
- Pythondaq:
scan
- Pythondaq: herhaalmetingen
- Pythondaq:
list
- Pythondaq:
info
- Pythondaq: choose device
- Pythondaq: Grafiek
- Pythondaq:
--help
Pythondaq: stapgrootte
Soms wil je snel een meting uitvoeren over het hele bereik, dan is het handig om minder punten te meten dan 1023 punten. Breid de applicatie uit zodat de gebruiker de stapgrootte kan aanpassen.
Het meetinstrument kiezen¶
We kunnen de Arduino benaderen als we de naam weten die de VISA driver er aan heeft toegekend. Helaas kan — ook afhankelijk van het besturingssysteem — die naam veranderen als we de Arduino in een andere poort van onze computer steken of soms zelfs als we een andere Arduino op dezelfde poort koppelen. Met het commando list
laten we alle apparaten zien die gevonden worden door de VISA drivers.
Pythondaq: list
Pas het subcommando list
aan.
De gebruiker test het subcommando list
met de volgende handelingen. De gebruiker typt het commando diode list
in de terminal. Daarna verschijnt in de terminal een lijst van aangesloten instrumenten.
(oefenopdrachten) > diode list
('ASRL28::INSTR','ASRL5::INSTR')
Checkpunten:
- De gebruiker kan met
diode list
de lijst met aangesloten devices opvragen.
Projecttraject:
- Pythondaq: commando's
- Pythondaq:
scan
- Pythondaq: herhaalmetingen
- Pythondaq:
list
- Pythondaq:
info
- Pythondaq: choose device
- Pythondaq: Grafiek
- Pythondaq:
--help
Pythondaq: info
Voeg een subcommando info
toe.
De gebruiker test het subcommando info
met de volgende handelingen. Eerst heeft de gebruiker met het commando diode list
een lijst van aangesloten devices opgevraagd. De gebruiker wil weten wat de identificatiestring is van het apparaat dat aan een bepaalde poortnaam hangt. De gebruiker geeft daarom de poortnaam mee als argument aan het subcommando info
waarna de identificatiestring van het instrument in de terminal wordt geprint.
identificatiestring
De identificatiestring van onze Arduino was Arduino VISA firmware v1.1.0
. Je moet natuurlijk niet letterlijk deze string copy/pasten, maar de identificatie opvragen van het instrument. Welk firmwarecommando moest je daarvoor ook alweer gebruiken?
Pseudo-code
Testcode(oefenopdrachten) > diode info ASRL28::INSTR
Arduino VISA firmware v1.1.0
Checkpunten:
- De identificatiestring is met
diode info DEVICE
op te vragen. - De string is niet direct gecopypaste, maar wordt daadwerkelijk opgevraagd.
Projecttraject:
- Pythondaq: commando's
- Pythondaq:
scan
- Pythondaq: herhaalmetingen
- Pythondaq:
list
- Pythondaq:
info
- Pythondaq: choose device
- Pythondaq: Grafiek
- Pythondaq:
--help
Pythondaq: choose device
Pas het subcommando scan
aan zodat je kan aangeven met welke Arduino je een meting wilt uitvoeren.
De gebruiker test het subcommando scan
met de volgende handelingen. De gebruiker typt het commando diode scan
in de terminal en vergeet daarbij een poortnaam mee te geven. De gebruiker ziet een foutmelding verschijnen want een poortnaam opgeven is verplicht.
De gebruiker vraagt met het subcommando list
een lijst van aangesloten instrumenten op. Met het subcommando info
is de gebruiker er achtergekomen wat de naam is van de poort waar de Arduino aanhangt. Vervolgens geeft de gebruiker deze poortnaam mee bij het subcommando scan
om een meting op de (juiste) Arduino uit te laten voeren.
Tot slot leent de gebruiker een Arduino van een buurmens. De gebruiker sluit de tweede Arduino aan op de computer. Met list
en info
kijkt de gebruiker wat de poortnaam is van de tweede Arduino. Met het subcommando scan
voert de gebruiker een meting uit en ziet dat het lampje van de tweede Arduino gaat branden en niet het lampje van de eerste Arduino.
(oefenopdrachten) > diode scan
errorUsage: diode [OPTIONS] DEVICE
Try 'diode --help' for help.
Error: Missing argument 'DEVICE'.
Checkpunten:
- De gebruiker moet een poortnaam meegeven.
- De gekozen device wordt ook daadwerkelijk gebruikt in het model en de controller.
- Als géén poortnaam wordt opgegeven, krijgt de gebruiker een foutmelding.
Projecttraject:
- Pythondaq: commando's
- Pythondaq:
scan
- Pythondaq: herhaalmetingen
- Pythondaq:
list
- Pythondaq:
info
- Pythondaq: choose device
- Pythondaq: Grafiek
- Pythondaq:
--help
Pythondaq: Grafiek
Pas het subcommando scan
aan zodat je met een boolean flag kan aangeven of er wel of niet een grafiek wordt getoond.
De gebruiker test het subcommando scan
met de volgende handelingen. De gebruiker start een meting en geeft ook de optie --graph
na afloop ziet de gebruiker een grafiek met daarin de metingen. Daarna start de gebruiker opnieuwe een meting en geeft dit keer de optie --no-graph
mee, na afloopt van de meting ziet de gebruiker geen grafiek verschijnen. Tot slot start de gebruiker een meting en geeft daarbij geen van beide opties (`--graph/--no-graph) wederom ziet de gebruiker na afloop van de meting geen grafiek verschijnen.
Checkpunten:
- De grafiek wordt alleen getoond wanneer
--graph
wordt meegegeven.
Projecttraject:
- Pythondaq: commando's
- Pythondaq:
scan
- Pythondaq: herhaalmetingen
- Pythondaq:
list
- Pythondaq:
info
- Pythondaq: choose device
- Pythondaq: Grafiek
- Pythondaq:
--help
Pythondaq: --help
Voeg helpteksten toe.
De gebruiker test de applicatie diode
met de volgende handelingen. De gebruiker typt diode --help
en bekijkt de helptekst. De gebruiker ziet dat er subcommando's zijn. Met subcommando --help
test de gebruiker de helpteksten een voor een uit. Ook bekijkt de gebruiker de helpteksten over de argumenten en opties. De helpteksten stellen de gebruiker in staat om de applicatie te begrijpen en te bedienen.
Pseudo-code
Testcode(oefenopdrachten) > diode --help
Usage: diode [OPTIONS] COMMAND [ARGS] ...
Options:
--help Show this message and exit.
Commands:
Subcommand Summary containing ARGUMENTs.
Checkpunten:
- De informatie in de helpteksten is voldoende om de applicatie te begrijpen en te bedienen.
-
diode --help
vertelt duidelijk welke subcommando's aanwezig zijn en wat ze doen. - Bij alle subcommando's, is het duidelijk welke opties en argumenten er zijn, wat de standaardwaarden zijn en wat ze doen.
Projecttraject:
- Pythondaq: commando's
- Pythondaq:
scan
- Pythondaq: herhaalmetingen
- Pythondaq:
list
- Pythondaq:
info
- Pythondaq: choose device
- Pythondaq: Grafiek
- Pythondaq:
--help
Pythondaq: list --search
Breid het commando list
uit met een optie --search
waarmee je niet een lijst van alle instrumenten krijgt, maar alleen de instrumenten die de zoekterm bevatten. Dus bijvoorbeeld:
(oefenopdrachten) > diode list
The following devices are connected to your computer:
ASRL/dev/cu.SOC::INSTR
ASRL/dev/cu.MALS::INSTR
ASRL/dev/cu.AirPodsvanDavid-Wireles-1::INSTR
ASRL/dev/cu.Bluetooth-Incoming-Port::INSTR
ASRL/dev/cu.usbmodem143401::INSTR
(oefenopdrachten) > diode list -s usbmodem
The following devices match your search string:
ASRL/dev/cu.usbmodem143401::INSTR
De lijst met instrumenten kan er op Windows heel anders uitzien. Sterker nog, op Windows is de lijst meestal vrij saai. Maar leen eens heel even een Arduino van iemand anders en je ziet dat er dan twee poorten in de lijst verschijnen.
Pas — na het uitbreiden van list
— de commando's scan
en info
aan zodat het niet nodig is om de volledige devicenaam mee te geven, maar alleen een zoekterm.
Op dit punt hebben we de functionaliteit van ons snelle script van het vorige hoofdstuk bereikt. Dit was veel meer werk, maar het is veel flexibeler. Als je wilt meten met een andere Arduino, een ander bereik, of een andere stapgrootte dan type je gewoon een iets ander commando in de terminal. Je hoeft geen scripts meer aan te passen. Als je na een tijdje niet meer precies weet hoe het ook alweer werkte allemaal kun je dat snel weer oppakken door --help
aan te roepen.
Alle subcommando's implementeren
Kijk nog eens terug naar het lijstje subcommando's die je in opdracht Subcommando's bedenken hebt opgeschreven. Heb je alles geïmplementeerd? Wat zou je willen dat je nog meer kan instellen? Als er tijd over is, kijk dan of dit lukt.
Rich
Een interface met stijl¶
Ook command-line interfaces gaan met hun tijd mee. Vroeger waren ze per definitie zwart/wit en statisch, maar tegenwoordig worden interfaces vaak opgeleukt met kleur, emoji's en bewegende progressbars. Rich7 is een project dat in recordtijd heel populair is geworden. Het bestaat pas sinds november 2019 en heeft precies twee jaar later meer dan 31000 verzameld. Dat is veel — en de populariteit is sindsdien nog verder toegenomen.
Rich is ontzettend uitgebreid en heeft heel veel mogelijkheden. Voor ons project kan het handig zijn om een progressbar te gebruiken of met Rich een tabel weer te geven. De documentatie8 van Rich is best goed, maar kan lastig zijn om een mooi overzicht te krijgen. Een serie van korte video tutorials kun je vinden bij calmcode. Iedere video duurt maar één tot twee minuten en laat mooi de mogelijkheden zien. Voor de functies die je wilt gebruiken kun je dan meer informatie opzoeken in de documentatie van Rich zelf.
Rich
Verrijk je interface met Rich. Doe dit naar eigen wens en inzicht.
Data-analyse
Data-analyse¶
Door de $I,U$-karakteristiek van de (lichtgevende) diode te analyseren is het mogelijk om de constante van Boltzmann te bepalen. De stoomsterkte door een diode wordt gegeven door de Shockley diodevergelijking. Zie ook hoofdstuk diode.
Lukt het, om binnen de te bepalen onzekerheid, overeenkomst te vinden met de literatuurwaarde? Een LED is helaas geen ideale diode dus dit kan lastig zijn.
Model fitten
Fit het model van Shockley aan je $I,U$-karakteristiek. Welke parameters kun je bepalen? Overleg met je begeleider!
-
Opties hebben vaak een lange naam en een afkorting. De lange naam wordt gegeven met twee mintekens en de korte naam met één minteken.
PS> python -V
wordt met de lange naamPS> python --version
. Beide commando's geven hetzelfde antwoord. Je kunt je voorstellen dat de korte naam minder typwerk is. In de handleiding gebruiken we echter meestal de lange naam van een optie, omdat het commando dan beter leesbaar wordt. ↩ -
argv staat voor: argument vector, een lijst met argumenten. ↩
-
Merk op:
_
is de weggooivariabele in Python. In het geval van de voorbeeldcode gaat het er om dat de for-loop een aantal keer doorlopen wordt, je hoeft verder niets te doen met de loop index. ↩ -
Zie ook: The Python Standard Library ↩
-
Zie voor meer informatie over flags de Click documentatie. ↩
-
Pallets. Click. URL: https://click.palletsprojects.com/. ↩
-
Will McGugan. Rich. URL: https://github.com/willmcgugan/rich. ↩
-
Will McGugan. Rich documentation. URL: https://rich.readthedocs.io/en/latest/. ↩