Door Koen Vervloesem

Freelanceredacteur

Gelikte code met PyScaffold

Python-projecten ontwikkelen als een pro

15-01-2023 • 06:00

61

Multipage-opmaak

Projectgenerator kiezen

Python is een laagdrempelige en krachtige programmeertaal, die veel gebruikt wordt in machinelearning, data-analyse, systeemautomatisering enzovoort. Ook voor hobbyisten is het een dankbare taal, bijvoorbeeld voor smarthometoepassingen. De taal Python kennen is echter nog niet te vergelijken met op een professionele manier een Python-project ontwikkelen en uitbrengen. Er bestaan allerlei best practices en hulpmiddelen om je daarbij te helpen, maar als je vijf Python-experts om hulp vraagt, krijg je waarschijnlijk vijf verschillende adviezen. Dus hoe begin je eraan om je software wat professioneler te gaan bouwen?

Daarvoor bestaan diverse ‘projectgenerators’ waarmee je een nieuw Python-project aanmaakt op basis van een sjabloon dat al allerlei best practices toepast rond documentatie, tests, packaging, afhankelijkheden, codestijl enzovoort. Als je hiermee van start gaat, zijn er al een boel keuzes voor je gemaakt en kun je onmiddellijk aan de slag. Naarmate je meer ervaring krijgt met de projectgenerator en de gemaakte keuzen, kun je daarna altijd bewust andere keuzen maken en van het sjabloon afwijken.

Projectsjablonen

Eén keuze moet je wel al vanaf het begin maken: met welke projectgenerator ga je werken? Uiteindelijk is dit ook een kwestie van smaak. Je hebt zelf als Python-ontwikkelaar waarschijnlijk al wat best practices en een programmeerstijl aangenomen en misschien al wat hulpmiddelen aan je toolbox toegevoegd. Als je dan een projectgenerator gaat gebruiken die daar veel van afwijkt, kan dat initieel tot frustraties leiden. Bekijk dus zeker eens de verschillende mogelijkheden en onderzoek welke het best bij je past.

Een van de bekendste (en oudste) projectgenerators is cookiecutter. Deze creëert een project op basis van een projectsjabloon en er bestaat een cookiecutter-sjabloon voor een Python-pakket. Twee wat modernere cookiecutter-sjablonen voor Python-projecten zijn python-package-template en wemake-python-package. Ze maken allebei gebruik van de tool Poetry voor het beheer van afhankelijkheden en het publiceren van je Python-pakket. Er bestaan ook domeinspecifieke cookiecutter-sjablonen, zoals cookiecutter-data-science.

Een andere populaire projectgenerator is PyScaffold. Deze is specifiek voor Python ontwikkeld en je hoeft er geen sjabloon meer bij te kiezen. Overigens kun je ook zonder een projectgenerator sjablonen gebruiken. Zo is er het project python-blueprint. Dit kopieer je eenvoudigweg om op basis daarvan je eigen Python-project op te bouwen. Het voordeel van een projectgenerator is echter dat je het standaardsjabloon kunt aanpassen aan de hand van enkele parameters. Bij een update van de projectgenerator kun je bovendien vernieuwingen eenvoudig in je eigen project integreren. Ben je gestart van een kopie van een sjabloon, dan heb je wat meer handwerk.

PyScaffold genereert een basisstructuur voor je Python-projecten op basis van een sjabloon.
PyScaffold genereert een basisstructuur voor je Python-projecten op basis van een sjabloon.

Aan de slag met PyScaffold

In de rest van dit artikel tonen we hoe je een Python-project start met PyScaffold. Ik heb daarvoor gekozen omdat PyScaffold het meest overeen bleek te komen met mijn stijl van ontwikkelen in Python en uitstekende documentatie heeft. In dit artikel ga ik ervan uit dat je al wat basiskennis van Python hebt, maar wees gerust: we gaan niet coderen. Het gaat hier vooral om het beheer van je project. Kies je een andere projectgenerator dan PyScaffold of een los sjabloon, dan verloopt het proces gelijkaardig. De best practices die ze toepassen en de hulpmiddelen waarvan ze gebruikmaken, zijn immers grotendeels gelijk.

PyScaffold is beschikbaar als pakket op PyPI, dus je installeert het eenvoudig met pip:

pip install pyscaffold

Ben je van plan om een van de officiële uitbreidingen op PyScaffold te gebruiken, installeer die dan mee:

pip install pyscaffold[all]

Dat is vooral nuttig als je geen liefhebber van reStructuredText bent, maar Markdown wilt gebruiken als opmaaktaal, of als je Travis CI voor continue integratie verkiest.

Installeer daarna ook tox, een tool om tests en package builds in virtuele omgevingen uit te voeren, en pre-commit, dat vóór elke commit met Git een hoop door PyScaffold geconfigureerde controles uitvoert:

pip install tox pre-commit

Repository initialiseren en projectconfiguratie

Voordat je met PyScaffold een projectdirectory aanmaakt, moet je je realiseren dat het programma er een lokale Git-repository van maakt. Zorg dus dat je globale configuratie voor Git in orde is. Stel minstens je naam en e-mailadres in en eventueel de standaard branch als je een andere dan 'master' wilt:

git config --global user.name "Jan Janssens"
git config --global user.email janjanssens@example.com
git config --global init.defaultBranch main

PyScaffold laat je daarna toe om met één opdracht een projectdirectory aan te maken, met de opdracht putup:

putup awesome_project

Dit maakt een directory awesome_project aan met het basissjabloon. Als je niet tevreden bent met de standaardkeuzes van PyScaffold, bekijk dan met putup -h de opties die het programma kent om het gedrag aan te passen.

Interactieve modus

Er is ook wat de makers van PyScaffold een ‘interactieve’ modus noemen:

putup -i awesome_project

Deze opdracht opent een configuratiebestand in de standaardeditor van je besturingssysteem, met de standaardopties al ingevuld. Deze kun je dan aanpassen. Commentaarregels, die beginnen met #, geven uitleg bij de opties. Verander bijvoorbeeld de beschrijving, de URL en de licentie. Al deze waarden kun je overigens achteraf ook nog aanpassen in de gegenereerde bestanden, maar dan is het wat meer zoekwerk doordat ze verspreid in verschillende bestanden staan.

Heel wat opties zijn in het configuratiebestand standaard uitgeschakeld doordat er een commentaarteken voor staat. Die schakel je dan in door het commentaarteken weg te halen. Maak je bijvoorbeeld gebruik van continue integratie om bij elk push of pull request je code te testen, dan kies je hier je CI-provider met --github-actions, --gitlab, --travis of --cirrus. Wil je je documentatie niet in reStructuredText, maar in Markdown schrijven, schakel dan ook --markdown in. Dit gebruikt overigens het Markdown-dialect MyST.

Nadat je het bestand hebt opgeslagen en je editor afsluit, creëert PyScaffold je projectdirectory met de ingestelde configuratie. Ga naar de aangemaakte directory en je zult zien dat PyScaffold ze als Git-repository heeft geïnitialiseerd met een commit met boodschap 'Initial commit'.

In de ‘interactieve’ modus van PyScaffold stel je het sjabloon voor je project in een configuratiebestand in.
In de ‘interactieve’ modus van PyScaffold stel je het sjabloon voor je project in een configuratiebestand in.

Pre-commit

Update als eerste in je project de pre-commit hooks naar de nieuwste versies, zodat je onmiddellijk de nieuwste versies hebt van de scripts die je commits controleren:

pre-commit autoupdate

Dit past de versienummers aan in het configuratiebestand .pre-commit-config.yaml dat PyScaffold heeft aangemaakt. Als je nu de aanpassingen commit met git commit -a, zul je zien dat de hooks geïnstalleerd worden en dat er daarna allerlei controles gebeuren voordat Git je editor opent om de commitboodschap in te voeren.

Dit gebeurt vanaf nu bij elke commit die je doet. Welke controles er worden uitgevoerd, hangt af van de bestanden die in de commit worden aangepast. Voor het YAML-bestand dat de auto-update heeft aangepast, wordt bijvoorbeeld gecontroleerd of de syntaxis correct is. Voor Python-bestanden worden echter Isort, Black en Flake8 uitgevoerd met de configuratie die PyScaffold heeft klaargezet.

Met isort worden je import-statements in een consistente volgorde geplaatst. Black is een code formatter, waardoor je code een consistente stijl heeft en je niet hoeft na te denken over waar je spaties plaatst en dergelijke. Flake8 waarschuwt je voornamelijk over code smells: dubieuze zaken die je waarschijnlijk beter aanpast.

Stel dat je wijzigingen in je code commit en een van de controles faalt, dan moet je dit eerst oplossen. Een waarschuwing door Flake8 is doorgaans duidelijk genoeg. Je hebt bijvoorbeeld een module geïmporteerd die je niet gebruikt, of je gebruikt een te algemene exception in een try-except-blok. Er staat ook altijd een unieke code bij die de regel waartegen je zondigt identificeert. Daarmee kun je meer informatie opvragen op de website https://www.flake8rules.com. Black en Isort voeren hun wijzigingen overigens automatisch al op je codebestanden uit. Nadat je de Flake8-waarschuwing hebt opgelost, of Black en Isort hun werk hebben gedaan, voeg je de gewijzigde bestanden opnieuw toe met git add en commit je weer. Als alles nu in orde is, gebeurt de commit. Op deze manier garandeer je dat code die je commit, vrij is van een heleboel stilistische en andere fouten.

Pre-commit controleert je code op heel wat fouten voordat je ze in je Git-repository commit.
Pre-commit controleert je code op heel wat fouten voordat je hem in je Git-repository commit.

Code aanpassen

De voorbeeldcode die door PyScaffold wordt aangemaakt, in de directory src, creëert een module skeleton met een functie 'fib' om het zoveelste getal uit de fibonaccireeks te berekenen. De module bevat ook een functie 'run', die wordt uitgevoerd als je de module na installatie op de opdrachtregel uitvoert, bijvoorbeeld met python -m awesome_project.skeleton 42. Je vindt er ook al een functie parse_args, die argumenten op de opdrachtregel afhandelt. Nu kun je dit bestand gaan aanpassen om er je eigen code van te maken. Het bestand skeleton.py zul je dan ook hernoemen naar die van je eigen module.

Metadata en afhankelijkheden van je project pas je aan in het bestand setup.cfg. Zaken als de licentie en korte beschrijving die je in de interactieve modus van PyScaffold hebt aangepast, zijn hier al ingevuld. Vergeet zeker de URL’s voor je project niet aan te passen. Die komen immers terecht in het pakket dat je bouwt en naar PyPI uploadt. Bij install_requires voeg je de afhankelijkheden van je pakket toe. Verder kun je extra afhankelijkheden voor je tests opnemen, zoals specifieke plug-ins voor pytest.

Ook interessant zijn de console scripts. Hier definieer je bijvoorbeeld met fibonacci = awesome_project.skeleton:run dat je bij installatie van het pakket een opdracht fibonacci wilt installeren die je op de opdrachtregel kunt uitvoeren en die dan de functie run van de module awesome_project.skeleton aanroept.

Een groot deel van de configuratie van je Python-pakket gebeurt in het bestand setup.cfg.
Een groot deel van de configuratie van je Python-pakket gebeurt in het bestand setup.cfg.

Tests en documentatie

Goede software heeft tests. Dat geldt dus ook voor het project dat PyScaffold voor je heeft aangemaakt. Je start de tests eenvoudigweg met:

tox

Dit maakt een virtuele omgeving aan waarin de tests worden uitgevoerd met pytest. Als je de standaardcode van de module skeleton al hebt aangepast, krijg je nu een foutmelding. De tests zoeken immers een module die niet meer bestaat. Vervang de testcode in tests/test_skeleton.py daarom door je eigen tests.

In het eenvoudigste geval kan testcode met pytest vrij kort zijn. De testcode om de functie fib te testen ziet er bijvoorbeeld als volgt uit:

import pytest

from awesome_project.skeleton import fib, main


def test_fib():
    """API Tests"""
    assert fib(1) == 1
    assert fib(2) == 1
    assert fib(7) == 13
    with pytest.raises(AssertionError):
        fib(-10)


def test_main(capsys):
    """CLI Tests"""
    # capsys is a pytest fixture that allows asserts against stdout/stderr
    # https://docs.pytest.org/en/stable/capture.html
    main(["7"])
    captured = capsys.readouterr()
    assert "The 7-th Fibonacci number is 13" in captured.out

Als je tox uitvoert, toont pytest je niet alleen welke tests (test_fib, test_main enzovoort) oké zijn en welke falen, maar je krijgt ook de test coverage te zien: hoeveel procent van de code wordt door al je tests uitgevoerd. Je probeert die test coverage zo hoog mogelijk te houden door telkens als je nieuwe functionaliteit toevoegt, bijbehorende testfuncties te schrijven.

Een goede testsuite geeft je vertrouwen in de werking van je Python-project en kan regressies na wijzigingen in je code detecteren.
Een goede testsuite geeft je vertrouwen in de werking van je Python-project en kan regressies na wijzigingen in je code detecteren.

Documentatie

Een ander belangrijk onderdeel van het project dat PyScaffold aanmaakt, is de documentatie die je in de directory docs vindt. Bekijk zeker alle bestanden hierin en ook de bestanden waarnaar ze verwijzen in de hoofddirectory, want er staan nog talloze todo’s in die je zelf moet invullen of die je moet verwijderen omdat ze niet van toepassing zijn op je project.

In de Readme worden standaard een hoop badges beschreven waarmee je icoontjes laat verwijzen naar de status van je continue integratie, je online documentatie, je project op PyPI enzovoort. Het bestand Contributing bevat al een heleboel informatie voor wie aan je project wil bijdragen. Lees dit goed door, geef hierin de juiste links op en voeg eventueel extra projectspecifieke informatie toe.

Als je nu HTML-pagina’s met de documentatie wilt genereren, doe je dat met de omgeving 'docs' van tox, die Sphinx aanroept:

tox -e docs

Het resultaat vind je in de directory docs/_build/html. Je kunt dit ook bekijken met behulp van Pythons ingebouwde webserver:

python3 -m http.server --directory 'docs/_build/html'

Je controleert overigens eenvoudig de geldigheid van alle links in je documentatie met:

tox -e linkcheck

De linkcheck van Sphinx controleert of alle url’s in je documentatie nog werken.
De linkcheck van Sphinx controleert of alle URL’s in je documentatie nog werken.

Docstrings en continue integratie

De docstrings in je code worden door Sphinx in de documentatie geplaatst op de pagina Module Reference. Vooral als je project een Python-bibliotheek is, is dit een handige manier om de api van je bibliotheek te documenteren voor gebruikers.

Als je doctests aan je docstrings hebt toegevoegd die voorbeelden tonen van de aanroep van je functies, kun je deze ook laten uitvoeren met:

tox -e doctests

Op deze manier garandeer je dat de voorbeelden die je in je documentatie toont, altijd werken.

De voorbeeldcode in deze documentatie wordt uit de docstrings van de code gehaald en getest op geldigheid.
De voorbeeldcode in deze documentatie wordt uit de docstrings van de code gehaald en getest op geldigheid.

Continue integratie

Desgevraagd maakt PyScaffold ook een configuratie aan voor continue integratie, of CI. Voor GitHub Actions is die configuratie te vinden in het bestand .github/workflows/ci.yml. Deze voert op elk push en pull request de pre-commit hooks uit, bouwt een pakket van je software en voert de tests met pytest op dit pakket uit, voor verschillende Python-versies en platforms (Ubuntu, Windows en macOS, al bevat de GitHub Actions workflow momenteel nog een fout). Er wordt een coverage report van de tests gegenereerd en geüpload naar Coveralls. Als je een Git-tag hebt gepusht, wordt die als versie geïnterpreteerd en wordt het gebouwde pakket met dit versienummer naar PyPI geüpload. Als een van deze stappen fout loopt, krijg je dit duidelijk in de webinterface van GitHub te zien en kun je doorklikken om de logs te bekijken.

Enkele van deze stappen vereisen nog wat configuratie. Zo moet je voor de automatische publicatie van je builds een api-token bij PyPI aanmaken en dit als secret met de naam PYPI_TOKEN in de instellingen van je repository invoeren. Het coverage report met Coveralls kreeg ik zelf niet in werking, maar ik heb de workflow aangepast om Codecov te gebruiken. Dat werkte zonder dat er een secret nodig was, omdat het om een publieke repository ging.

Om de online documentatie te publiceren, heb je overigens geen continue integratie nodig. Importeer in Read the Docs gewoon je publieke repository. PyScaffold heeft al een configuratiebestand .readthedocs.yml aangemaakt, waarmee Read the Docs voldoende heeft om je documentatie aan te maken. Bij elke push naar de standaard branch van je repository genereert Read the Docs dan je documentatie met Sphinx en publiceert deze op naamvanproject.readthedocs.io.

Door je code in GitHub Actions op alle Python-versies en besturingssystemen te testen die je ondersteunt, ontdek je eenvoudig versie- of platformspecifieke fouten.
Door je code in GitHub Actions te testen op alle Python-versies en besturingssystemen die je ondersteunt, ontdek je eenvoudig versie- en platformspecifieke fouten.

Pakket publiceren en conclusie

Als je liever niet automatisch je project op PyPI publiceert in je CI-workflow, verwijder dan de job publish uit het configuratiebestand voor GitHub Actions. Je kun dit immers ook nog handmatig doen met behulp van tox. Zorg er eerst voor dat tox je tests succesvol uitvoert, tag dan je huidige commit en push deze naar je repository:

git tag v1.2.3
git push origin v1.2.3

Verwijder dan tijdelijke bestanden van een vorige build van het pakket en/of de documentatie en bouw het pakket:

tox -e clean
tox -e build

Dit bouwt het pakket met als versienummer de tag die je zojuist hebt aangemaakt. Het resultaat is een bestand awesome_project-1.2.3.tar.gz in de directory dist. Dit kun je nu op PyPI publiceren met:

tox -e publish -- --repository pypi

Elke nieuwe versie van je project publiceer je eenvoudig automatisch of manueel op PyPI.
Elke nieuwe versie van je project publiceer je eenvoudig automatisch of handmatig op PyPI.

Aanpassen en conclusie

Een bestaand project kun je ook naar PyScaffold omzetten. PyScaffold maakt dan de hele projectstructuur aan met behoud van je bestaande bestanden. Dan moet je dit alles nog handmatig integreren. Als je bijvoorbeeld eerder je project in setup.py heb geconfigureerd, moet je die parameters in het door PyScaffold aangemaakte setup.cfg integreren.

Als PyScaffold zelf met een update van het sjabloon komt, kun je dit in je project toepassen met:

putup --update

Sowieso is de configuratie die PyScaffold aanmaakt maar een voorbeeld. Je kunt er perfect stappen aan toevoegen of de bestaande stappen aanpassen aan de behoeften van je project. Zo voeg ik zelf in projecten gebaseerd op PyScaffold doorgaans Pylint en een boel Flake8-plug-ins aan pre-commit toe voor extra controles, evenals mypy om typen te controleren.

Kortom, PyScaffold is een handig hulpmiddel om een projectstructuur voor Python-projecten aan te maken dat al heel wat best practices toepast. De configuraties voor pre-commit en continue integratie geven je een goede basis om robuuste software te ontwikkelen. Zelfs al stap je ooit van de structuur van PyScaffold af, je leert er heel wat mee bij dat de kwaliteit van je Python-projecten verbetert.

Reacties (61)

61
58
42
2
0
9
Wijzig sortering
Leuk artikel! Ik kende pyscaffold nog niet, maar ben er zelf niet zo'n groot fan van als ik het zo zie. Volgens mij beweegt Python al een tijdje naar de standaard dat project metadata in pyproject.toml komt te staan, maar met deze tool zit je vast aan setup.cfg. Ook jammer dat het niet de optie lijkt te bieden om Poetry te gebruiken voor dependency management. Sinds ik daarmee ben begonnen wil ik eigenlijk niet meer terug.

Voor wie geintresseerd is: Ik heb zelf recent ook een cookiecutter template ontwikkeld voor python projecten: cookiecutter-poetry. Iets completer dan wemake-python-package en hij bevat ook vast de eerste drie todo's van python-package-template (wij van WC Eend... :))

[Reactie gewijzigd door flo12392 op 22 juli 2024 14:43]

Kun je misschien toelichten waarom je niet meer terug wilt van poetry? De keren dat ik het gebruikt heb zag ik weinig tot geen voordelen.
Poetry maakt je dependencies expliciet (in pyproject.toml) en creëert automatisch een virtuele environment waarin deze dependencies beschikbaar zijn. Dus niet meer handigmatig pip freeze en python -m venv gebruiken. Met deze laatste methode kunnen er eenvoudig per ongeluk dependencies in je virtuele environment slippen als je pip install uitvoert in de verkeerde environment. Ik heb vaak genoeg projecten gezien waarbij er tientallen packages in requirements.txt staan terwijl er maar een handvol daadwerkelijk nodig zijn. Dit is niet goed voor de onderhoudbaarheid.

Ook bevat een pyproject.toml-bestand informatie over de vereiste Python-versie. Een Poetry virtuele environment werkt daarom alleen maar met de vooraf gedefinieerde Python-versie, wat problemen voorkomt als mensen met Python 3.8 een project ontwikkeld in Python 3.7 bijvoorbeeld. Poetry zorgt er dan voor dat automatisch de virtuele environment met Python 3.7 aangemaakt wordt, zoals vooraf gedefinieerd.

Het grote voordeel is overdraagbaarheid van de code naar andere systemen. Met een pyproject.toml heb je expliciet alle instructies in huis om op welke machine dan ook de code werkend te krijgen en hoef je niet op zoek naar ongestructureerde handgeschreven documentatie die vaker wel dan niet afwezig is.
Bedankt voor de info!

Nog een aanvullende vraag over het eerste deel n.a.v. je uitleg:
Poetry activeert niet automatisch de virtualenv (dat kan ook eigenlijk niet, aangezien dit puur een shel/env vars wijziging is), hoe lost het dan het probleem op van per ongeluk in de verkeerde virtualenv dingen installeren? Als je virtualenv 1 geactiveerd hebt voor project 1 en dan in project 2 `poetry add x` doet zonder virtualenv 2 te activeren wordt deze gewoon in virtualenv 1 geinstalleerd. Of bedoelde je iets anders?

[Reactie gewijzigd door aaahaaap op 22 juli 2024 14:43]

Je hoeft de virtuele environment in principe nooit te activeren. Dependencies kan je installeren met "poetry add" en je kan Python appliaties uitvoeren met "poetry run python <...>". Omdat dependencies expliciet zijn zie je vrijwel meteen dat er een ongewenste dependency is geïnstalleerd zodra je de code commit naar Git. Dat is een andere workflow dan 1x in de zoveel tijd pip freeze uitvoeren.
Het is trouwens wel mogelijk om de virtuele environment te activeren met "poetry shell". Kan handing zijn om met meerdere commando's niet elke keer "poetry run" ervoor te hoeven zetten.

Verder zelf ook een groot fan van poetry. Enige nadeel is het draaien binnen een arm32 omgeving. Verschillende packages waar poetry gebruik van maakt, hebben niet de prebuild binaries voor arm32. Dan moet je dus de toolchain voor zowel c als rust installeren in je docker image en dan loop je ook nog tegen een QEMU bug aan die tegen de gigabestanden van de package manager van rust aanloopt en moet je naar een nightly rust switchen om gebruik te maken van de sparse registry feature van rust en omdat je je docker images klein wil houden, is het wijs om je dockerfile te splitten in een multi-stage. Verder een toptool!
Dependencies toevoegen doe je via `poetry add`. Dit werkt alleen als je shell op een locatie staat waar er een project.toml aanwezig is en je voegt dan ook toe voor die repo / venv (los van welke venv er al dan niet actief is).
Dit alles maakt me er steeds meer van bewust hoe grote nachtmerrie dependency management in python is. Ik gebruik nu een tijdje Julia en daar gaat dat allemaal een stuk vloeiender...
Niet alleen maakt poetry je dependancies expliciet zoals @MarcoC hieronder aangeeft, het heeft ook een commandline tool waarin je direct ziet welke versies incompatible zijn met elkaar.

Voorbeeld, hier installeren we de volgende packages in de global space met pip:

pip install 'numpy==1.15'
pip install 'pandas==0.25.3'

nu hebben we dus die twee dependancies (met subdependancies) geinstalleerd zoals je kan zien met pip list:

pip list
Package Version
--------------- --------
numpy 1.15.0
pandas 0.25.3
pip 19.0.3
python-dateutil 2.8.2
pytz 2022.7.1
setuptools 40.8.0
six 1.16.0


Wat is nu de grap ? als je nu pandas upgrade, upgrade je ook stilletjes numpy. Voorbeeld:

pip install 'pandas==1.3.4'

geeft:

Installing collected packages: numpy, pandas
Found existing installation: numpy 1.15.0
Uninstalling numpy-1.15.0:
Successfully uninstalled numpy-1.15.0
Found existing installation: pandas 0.25.3
Uninstalling pandas-0.25.3:
Successfully uninstalled pandas-0.25.3
Successfully installed numpy-1.21.6 pandas-1.3.5


oftewel: ook al had jij netjes je requirement.txt geupdate met je pip freeze, dan wordt deze alsnog stilletjes ge-upgrade.

Met poetry gebeurt t volgende :

poetry init .
poetry add numpy==1.15
poetry add pandas==0.25.3
poetry add pandas==1.3.4

in plaats van stilletjes te upgraden, geeft poetry nu netjes een waarschuwing:

Updating dependencies
Resolving dependencies... (0.0s)

SolverProblemError

Because pandas (1.3.4) depends on numpy (>=1.17.3)
and temp-poetry depends on numpy (1.15), pandas is forbidden.
So, because temp-poetry depends on pandas (1.3.4), version solving failed.

Ook heel prettig om de dependency chains te zien is `poetry show --tree`
Bedankt voor de info. Opzich doet pip wat je vraagt, packages die je zelf installeert zijn niet speciaal en worden dus niet los getracked, dus zolang er geen andere dependency op numpy is die numpy<1.21.6 required is er geen reden om numpy niet te upgraden.
Moet eerlijk zeggen dat ik in de praktijk nog nooit issues hiermee gehad heb.
Poetry behandelt blijkbaar de packages die je zelf hebt geinstalleerd anders dan dependencies van deze packages. Opzich is daar wel wat voor te zeggen, maar heb het tot nu toe iig niet gemist.

Heb je toevallig ook ervaring met pdm (https://pdm.fming.dev/latest/)?

[Reactie gewijzigd door aaahaaap op 22 juli 2024 14:43]

Oh wij hebben een aantal keer breaking bugs gehad met pakket A dat netjes gevalideerd was op b.v. numpy 1.0, pakket B upgrade numpy en daardoor deed pakket A het niet meer.

Als je de individuele packages schrijft heb je er amper last van, maar als je alle pakketten op 1 systeem probeert te installeren wel.

met poetry installeer je alle packages eigenlijk is een soort virtualenvironment bubble maar je hoeft er zelf niets voor op te zetten. Zo kan je dus pakket A en B met verschillende versies van b.v. numpy installeren naast elkaar, zonder dat je 1 'globale' numpy gebruikt (waardoor je ook geen conflicten hebt).

Geen ervaring met PDM, bedankt voor het delen, zal er eens naar kijken!

Zie b.v. voorbeeld: https://github.com/MrFronk/example-global-commands om te zien hoe je 2 packages kan installeren op t zelfde systeem met conflicting dependancies die toch netjes naast elkaar kunnen werken

[Reactie gewijzigd door Fronkie op 22 juli 2024 14:43]

pyscaffold genereert ook gewoon een pyproject.toml hoor.

Het is in de Python wereld altijd lastig om keuzes te maken, er is veel te kiezen.
Een pyproject.toml genereren is niet hetzelfde als die gebruiken voor alle project metadata. Het voorbeeld uit de PyScaffold documentatie bevat setup.py, setup.cfg, pyproject.toml en .isort.cfg; alles wat daarin staat kan in een enkel pyproject.toml bestand.

Je hebt zeker gelijk dat er (te) veel te kiezen is, gegeven dat je een pyproject.toml bestand gebruikt heb je tegenwoordig al de keuze uit Poetry, PDM, Flit of Hatch (en misschien nog wel meer?). Maar dat pyproject.toml als standaard gebruikt gaat worden voor dependency specificatie lijkt me inmiddels wel onvermijdelijk; ook setuptools plaatst tegenwoordig de voorbeelden van pyproject.toml voor die van setup.cfg.
Inderdaad, Poetry is fantastisch: https://python-poetry.org/

Het beheert niet alleen de dependencies (op een soortgelijke manier als npm dat doet voor JavaScript), maar ook je virtual environments en packaging, inclusief upload naar PyPI. Sinds ik het gebruik heb ik geen enkel probleem meer gehad met versies (voorheen gebruikte ik meerdere requirements.txt files, met en zonder precieze versieaanduiding) en hoef ik ook niet meer handmatig virtualenv, pip, twine, setup.py/cfg aan te roepen of te creëren.
Ik gebruik ook altijd Poetry. Heeft me nog nooit in de steek gelaten :) .
Eens! De “alles in één pyproject” approach is echt ideaal vanwege de overzichtelijkheid.

En vergeet niet dat je dan ook gelijk Deptry geïntegreerd hebt met de genoemde template😉. Je staat er lang niet altijd bij stil hoeveel (silent) dependency problemen er op de achtergrond spelen.
Dat die template deptry bevat is niet geheel toevallig; dat is mijn andere open-source Python project :P (Of was daar de knipoog voor?)
Is dit een nieuw type artikel? Het doet mij erg denken aan eerdere DIY artikelen maar toch even net wat anders. Mooi om wat diversificatie te zien zo. Is er al iets bekend over plannen van meer van dit soort artikelen en waar ze over zullen gaan?
Dit lang nog op de plank uit de Tweakers Plus-periode :) Dit soort artikelen vereist specifieke expertise en dus betaalden we freelancers om ze te schrijven. Nu Plus gestopt is, kunnen we dat niet meer doen. Dus fijn dat je deze kon waarderen, maar verwacht geen opvolging.
Ik was ook verbaasd, maar als je even kijkt naar de artikelen die deze freelancer voor Tweakers geschreven heeft dan kom ik tot de conclusie dat Koen een hobbyist is die graag over zijn projecten schrijft, en in dit geval lijkt hij zelf op zoek te zijn gegaan naar een tool en zijn ervaringen met ons te delen. Dat komt goed uit, want veel Tweakers lezen daar graag over.
Gratis Tweakers Plus :)
Is PyScaffold de Python evenknie van PHP's Composer (maar dan voor Python natuurlijk)? (https://getcomposer.org/)

[Reactie gewijzigd door CH4OS op 22 juli 2024 14:43]

Composer is een package manager, dus de Python evenknie daarvan zou denk ik Pip zijn.
Is Pip niet system-wide? Composer wordt (voornamelijk) per project gebruikt.
Pip kan packages ook voor één virtual environment installeren. Dan is het alleen voor dat project.
De evenknie van Composer is Pipenv omdat er een lock file is met oa checksums.
Ik denk dat je bij Python niet echt kunt spreken van 'de' evenknie; Poetry creëert bijvoorbeeld ook een lock file.
Leuk artikel! Goed om te zien ook dat ook aspecten zoals tests schrijven aan bod komt. Iets wat soms nog wel eens in de vergetelheid wil raken en essentieel als je professioneel wilt gaan programmeren :)
Ja testen worden nog al wel eens overgeslagen ... Maar je zou versteld staan hoeveel software er is zonder ook maar enige test en dat heeft niks met professioneel te maken.
Ik zeg niet dat het niet nuttig is, maar ik heb ook al draken van tests gezien waar 99% gemocked werd om dan 1 lijntje te testen. Dat gaat er voor mij. Net hetzelfde als een externe api ofzo, die geen test omgeving hebben. Tsja wat is het nut dan van die test ? Ik ben all for unit testing, tot op een bepaald punt.

Het grootste probleem is tijdsdruk. Wat zal er eerst worden overgeslagen als iets gisteren af moet ? Unit testing en documentatie. We zullen dit wel doen als er tijd is ... guess what: er is geen tijd, want het volgende moest ook gisteren af. Dus na verloop van tijd is er nog 75% van de code dat testen heeft, en dat blijft slinken, tot op een bepaald punt de testen gewoon opgegeven worden.

Ik ben van het principe: oftewel doe ik het goed, oftewel doe ik het niet. Na 25 jaar heb ik al veel dingen gezien. Een quick fix wil ik nog gerust doen, als iedereen daar mee verder of terug kan werken. Maar die quick fix gaan we meteen ook correct oplossen. Doe je dat niet dan hangt je programma vol met "tec 7" en "pattex". En als de "baas" het niet goed vind, dat is zijn probleem. Daar lig ik ook niet meer wakker van, wil je rommel, dan moet je niet bij mij zijn.
Hear, hear! Ik ben hier ook voor en neem vaak ook de tijd om iets goed op te lossen. Mijn probleem is echter dat ik altijd in een bestaande codebase terecht kom en 9 van de 10 keer zijn die al niet meer te redden. Ik vind het verbazingwekkend hoe slordig sommige mensen kunnen programmeren en waar ik helemaal van achterover sla is dat ze oprecht het probleem daar niet van zien: Scope van variabelen fucked? Geeft niks, als het maar werkt. Code all over the place copy/pasten? Waarom niet? Code fatsoenlijke namen geven of uitlijnen? Zonde van de tijd.
Ik heb juist het volledig tegenover gestelde. Het gaat hier om een Android codebase.

Alles is zo strak geïntegreerd dat je geen kant meer op kunt. Mijn voorgangers hebben veel te veel van de UI willen hergebruiken en daarom is alles maar een RecyclerView (een lijstweergave) geworden. Er is eigenlijk altijd maar meer code bijgekomen, zonder na te denken over het afbakenen van het domein. Mijn opdracht is nu het ontvlechten en upgraden van stukken code, maar ik ben er ook achter waarom ze dat steeds hebben gedaan. Het is gewoon niet te doen om oude code te refactoren, dus dan plak je er (logischerwijs /s) maar steeds meer aan vast. Daardoor krijg je code als een ui: heel veel laagjes.

Eerst probeer ik altijd de business value (externe kwaliteit) af te dekken (als ik op een knop druk, dan wordt deze waarde geregistreerd bijvoorbeeld) en daarna ga ik pas unit testen (interne kwaliteit). Het aller belangrijkste vind ik dat er niks stuk gaat voor de gebruiker. Daar is ook nog wat onderscheid in te maken, bijvoorbeeld het migreren van data. Dat is meer non-visueel, maar dat moet ook goed getest zijn, want het beïnvloedt de gebruikers ervaring direct.

Als je meer wil leren over welke testen je zou moeten schrijven, dan kan ik hartelijk het boek Growing Object-Oriented Software aanraden.
Eens, het begint uiteindelijk in de basis dat development tests schrijven mee neemt in de estimates die ze maken. TDD is nog mooier want dan draai je het development proces om.

Maar goed uiteindelijk is dit allemaal 'ideale wereld'. Veel developers zitten in een legacy situatie waarbij tijd altijd een probleem is en "omdat het vroeger ook kon, waarom nu dan niet?" vanuit hoger hand. Daar heb je dan een goede hervorming voor nodig, met standvastige leiders en uiteindelijk een organisatie die wil luisteren.
Ik wil ook mijn waardering uitspreken voor dit artikel, top! Ik doe veel in Python voor AppDaemon (icm HASS) maar dat is toch allemaal voornamelijk uit de losse pols :*)
Mooi artikel en graag nog meer van dit. :>
Erg leuk om over te lezen. Heb python veel gebruikt voor data analyses en functionele interne code, maar maak nooit apps. Zou wel willen weten waar ik over meer best practices en hulpmiddelen voor apps kan leren zoals PyScaffold.
Leuk artikel met alles goed beschreven, in vogelvlucht . Ook voor de Python beginner zonder ervaring met project-gebaseerd werken (ahem).
Een heel goed artikel! Mijn complimenten. Heel geschikt om de overgang van 'spelen met code' naar onderhoudbare software gemakkelijker te maken.
Waardering voor dit toffe artikel! Meer please.

Op dit item kan niet meer gereageerd worden.