Een praktische gids voor het gebruik van Setup.py

Data & AI Training Guide 2021

Download de GoDataDriven-brochure voor een volledig overzicht van de beschikbare trainingen en de leertrajecten voor data-engineering, datawetenschap, data-analist en analytics-vertaler.

Wanneer u python op professionele wijze gebruikt, loont het de moeite om uw projecten
op een consistente manier op te zetten. Dit helpt uw medewerkers om snel de
structuur van een project te begrijpen, en maakt het gemakkelijker voor hen om het project
op hun machine op te zetten. De sleutel tot het opzetten van uw project is het setup.py bestand.
In deze blog ga ik in op de details van dit bestand.

Waar we beginnen

Hierbij ga ik ervan uit dat u al een package heeft die u wilt opzetten.
Dit hoeft geen afgerond package te zijn – idealiter maakt u het
setup.py lang voordat uw project af is. Het kan zelfs een leeg pakket zijn;
zorg er alleen voor dat de pakketmap bestaat
en een bestand bevat met de naam init.py (dat leeg mag zijn).

Als u de structuur
van mijn collega Henk voor uw project volgt, zou uw beginsituatie er ongeveer zo uit moeten zien:

example_project/├── exampleproject/ Python package with source code.│ ├── __init__.py Make the folder a package.│ └── example.py Example module.└── README.md README with info of the project.

U mag andere bestanden of mappen in uw structuur hebben, bijvoorbeeld
mappen met de namen notebooks/, tests/ of data/, maar deze zijn niet vereist.

Het geval voor een setup.py

Als u eenmaal een pakket als dit hebt gemaakt, dan
zal u waarschijnlijk een deel van de code op andere plaatsen gebruiken. U zou bijvoorbeeld
dit kunnen doen in een notebook:

Dit zou werken als uw huidige werkdirectory example_project/ is, maar in
alle andere gevallen zal python u uitvoer geven zoals:

ModuleNotFoundError: No module named 'exampleproject'

Je zou python kunnen vertellen waar het pakket gezocht moet worden door de PYTHONPATH
omgevingsvariabele in te stellen of het pad toe te voegen aan sys.path,
maar dat is verre van ideaal: het zou verschillende acties vereisen op verschillende
platforms, en het pad dat je moet instellen is afhankelijk van de locatie van je code.
Een veel betere manier is om uw pakket te installeren met een setup.py en pip,
sinds pip de standaard manier is om alle andere pakketten te installeren, en het is gebonden
het werkt hetzelfde op alle platforms.

Een minimaal voorbeeld

Zo hoe ziet een setup.py bestand er uit? Hier is een minimaal voorbeeld0:

from setuptools import setup, find_packagessetup( name='example', version='0.1.0', packages=find_packages(include=))

Hier specificeren we drie dingen:

  • De naam van het pakket, dat is de naam die pip zal gebruiken voor uw pakket.
    Dit hoeft niet hetzelfde te zijn als de mapnaam waarin het pakket
    leeft, hoewel het verwarrend kan zijn als het dat niet is. Een voorbeeld van waar de package
    naam en de directory niet overeenkomen is Scikit-Learn: u installeert het
    met pip install scikit-learn, terwijl u het gebruikt door te importeren uit sklearn.
  • De versie van uw package. Dit is de versie die pip zal rapporteren, en wordt gebruikt
    bij voorbeeld wanneer u uw pakket publiceert op PyPI1.
  • Welke pakketten u moet includen; in ons geval is dat gewoon exampleproject/.
    Hier laten we setuptools dit
    automatisch uitzoeken2. Hoewel u find_packages()
    in principe zonder argumenten zou kunnen gebruiken, kan dit er mogelijk toe leiden dat ongewenste pakketten
    worden opgenomen. Dit kan bijvoorbeeld gebeuren
    als u een __init__.py in uw tests/
    directory hebt opgenomen. U kunt ook het exclude-argument gebruiken om expliciet
    te voorkomen dat tests in het pakket worden opgenomen, maar dit is iets
    minder robuust.

Het enige dat u nu nog hoeft te doen om uw pakket te installeren, is het volgende
uitvoeren vanuit de example_project/ directory3:

pip install -e .

De . hier verwijst naar de huidige werkdirectory, waarvan ik aanneem dat het de directory
is waar de setup.py te vinden is. De -e vlag geeft aan dat we
in bewerkbare modus willen installeren, wat betekent
dat wanneer we de bestanden in ons pakket bewerken, we het
pakket niet opnieuw hoeven te installeren voordat de wijzigingen van kracht worden. U moet
python wel opnieuw opstarten of het pakket opnieuw laden!

Wanneer u informatie in de setup.py zelf wijzigt, moet u het pakket in de meeste gevallen opnieuw
installeren, en ook als u nieuwe (sub)pakketten toevoegt.
Wanneer u twijfelt, kan het nooit kwaad om opnieuw te installeren. Voer pip install -e . gewoon opnieuw uit.

Requirements

De meeste projecten hebben enkele dependencies. U hebt waarschijnlijk al eerder een requirements.txt
bestand gebruikt, of een environment.yml
als u conda gebruikt. Nu u een setup.py maakt, kunt u uw
afhankelijkheden opgeven in het install_requires argument.
Voor een typisch data science project hebt u bijvoorbeeld:

setup( name='example', version='0.1.0', packages=find_packages(include=), install_requires=)

U kunt vereisten opgeven zonder een versie (PyYAML), een versie vastpinnen (pandas==0.23.3), een minimum
versie opgeven ('numpy>=1.14.5) of een bereik van versies instellen (matplotlib>=2.2.0,<3.0.0). Deze
vereisten worden automatisch door pip geïnstalleerd wanneer u uw pakket installeert.

Extras-require

Soms kunt u afhankelijkheden hebben die alleen in bepaalde situaties vereist zijn. Als data scientist
maak ik vaak pakketten die ik gebruik om een model te trainen. Als ik interactief
aan zo’n model werk, kan het zijn dat ik matplotlib en jupyter geïnstalleerd moet hebben om interactief met de
gegevens te kunnen werken en
visualisaties
van de prestaties van het model te kunnen maken. Aan de andere kant, als het model in productie
draait, wil ik matplotlib noch jupyter installeren op de machine (of container) waar ik
train of inferentie doe. Gelukkig staat setuptools toe om optionele afhankelijkheden op te geven in extras_require:

setup( name='example', version='0.1.0', packages=find_packages(include=), install_requires=, extras_require={ 'interactive': , })

Nu als we het pakket normaal installeren (pip install example van PyPI of pip install -e . lokaal)
zal het alleen de afhankelijkheden PyYAML, pandas en numpy installeren. Wanneer we echter
specificeren dat we de optionele interactive afhankelijkheden willen (pip install example
of pip install -e .),
dan zullen matplotlib en jupyter ook worden geinstalleerd.

Scripts en toegangspunten

Het belangrijkste gebruik van de meeste python pakketten die u installeert van PyPI is om functionaliteit
te bieden die kan worden gebruikt in andere python code. Met andere woorden, je kunt import van die pakketten gebruikmaken.
Als datawetenschapper maak ik vaak pakketten die niet bedoeld zijn om door andere python-code te worden gebruikt, maar
bedoeld zijn om iets te doen, bijvoorbeeld om een model te trainen. Als zodanig heb ik vaak een python-script dat
ik vanaf de opdrachtregel wil uitvoeren.

De beste manier4 om functionaliteit van je pakket aan de opdrachtregel bloot te stellen, is om
een entry_point als volgt te definiëren:

setup( # ..., entry_points={ 'console_scripts': })

Nu kun je het commando my-command vanaf de opdrachtregel gebruiken, dat op zijn beurt de main
functie in exampleproject/example.py zal uitvoeren. Vergeet niet opnieuw te installeren – anders wordt het commando
niet geregistreerd.

Tests

Wanneer u code schrijft, moedig ik u sterk aan om ook tests voor deze code te schrijven. Voor het testen
met python stel ik voor dat u pytest gebruikt. Natuurlijk wilt u pytest niet toevoegen aan uw dependencies
in install_requires: het is niet nodig voor de gebruikers van uw pakket. Om het
automatisch te laten installeren wanneer u tests uitvoert, kunt u het volgende toevoegen aan uw setup.py:

setup( # ..., setup_requires=, tests_require=,)

Extra moet u een bestand met de naam setup.cfg maken met de volgende inhoud:

test=pytest

Nu kunt u gewoon python setup.py test uitvoeren en setuptools zal ervoor zorgen dat de nodige afhankelijkheden
worden geïnstalleerd en pytest voor u uitvoeren! Kijk hier als
u argumenten wilt geven of configuratie opties wilt instellen voor pytest.

Als u extra vereisten hebt voor het testen (b.v. pytest-flask) kunt u ze toevoegen aan tests_require.

Flake8

Persoonlijk denk ik dat het een goed idee is om Flake8 te draaien om
de opmaak van uw code te controleren. Net als met pytest, wilt u flake8 niet toevoegen aan de
install_requires afhankelijkheden: het hoeft niet geïnstalleerd te zijn om uw
pakket te kunnen gebruiken. In plaats daarvan kunt u het toevoegen aan setup_requires:

setup( # ..., setup_requires=)

Nu kunt u gewoon python setup.py flake8 uitvoeren. Natuurlijk kunt u ook de versie
van flake8 (of een ander pakket) in setup_requires.

Als u enkele configuratieparameters van Flake8 wilt wijzigen, kunt u een sectie toevoegen aan
uw setup.cfg. Bijvoorbeeld:

max-line-length=120

Pakketgegevens

Soms kan het zijn dat u enkele niet-python bestanden in uw pakket wilt opnemen. Dit
kunnen bijvoorbeeld schema-bestanden zijn of een kleine opzoektabel. Wees u ervan bewust dat dergelijke bestanden
samen met uw code zullen worden verpakt, dus het is in het algemeen een slecht idee om
alle grote bestanden op te nemen.

Voorstel dat we een schema.json in ons project hebben, die we in exampleproject/data/schema.json plaatsen.
Als we dit in ons pakket willen opnemen, moeten we het package_data-argument van setup gebruiken:

setup( # ..., package_data={'exampleproject': })

Dit zal ervoor zorgen dat het bestand in het pakket wordt opgenomen. We kunnen er ook voor kiezen om
alle bestanden op te nemen op basis van een patroon, bijvoorbeeld:

setup( # ..., package_data={'': })

Dit zal alle *.json bestanden toevoegen in elk pakket dat het tegenkomt.

Probeer nu niet zelf uit te zoeken waar de geïnstalleerde bestanden zich bevinden, want
pkg_resources heeft enkele zeer handige handige functies:

  • pkg_resources.resource_stream geeft u een stream van het bestand, vergelijkbaar met het
    object dat u krijgt wanneer u open() aanroept,
  • pkg_resources.resource_string geeft u de inhoud van het bestand als een string,
  • pkg_resources.resource_filename geeft u de bestandsnaam van het bestand (en pakt
    het uit in een tijdelijk als het in een gezipt pakket zit) voor als de twee opties
    hierboven niet aan uw behoeften voldoen.

Zo zouden we bijvoorbeeld ons schema kunnen inlezen met:

from json import loadfrom pkg_resources import resource_streamschema = load(resource_stream('exampleproject', 'data/schema.json'))

Metadata

Als u uw pakket gaat publiceren, dan wilt u uw
potentiële gebruikers waarschijnlijk wat meer informatie geven over uw pakket, waaronder een beschrijving,
de naam van de auteur of de onderhouder, en de url naar de homepage van het pakket.
Een volledige lijst van alle toegestane metagegevens is te vinden in de setuptools
documenten.

Aanvullend, als u gaat publiceren naar PyPI, dan wilt u wellicht
automatisch de inhoud van uw README.md
in de long_description laden,
en classifiers meegeven om pip nog
meer over uw pakket te vertellen.

Wrap-up

Deze blog zou een goed startpunt moeten zijn om de meeste van uw python projecten op te zetten.
Als u meer wilt lezen over python packaging kijk dan
in de docs. Hier is een voorbeeld setup.py
waarin alle onderdelen uit deze blog zijn gecombineerd:

from setuptools import setup, find_packagessetup( name='example', version='0.1.0', description='Setting up a python package', author='Rogier van der Geer', author_email='[email protected]', url='https://blog.godatadriven.com/setup-py', packages=find_packages(include=), install_requires=, extras_require={'plotting': }, setup_requires=, tests_require=, entry_points={ 'console_scripts': }, package_data={'exampleproject': })

en de bijbehorende setup.cfg:

test=pytestmax-line-length=120

Verbeter uw Python vaardigheden, leer van de experts!

Bij GoDataDriven bieden we een groot aantal Python cursussen aan van beginner tot expert, gegeven door de allerbeste professionals in het veld. Doe mee en verbeter je Python-spel:

  • Python Essentials – Geweldig als je net begint met Python.
  • Certified Data Science with Python Foundation – Wil je de stap maken van data-analyse en visualisatie naar echte data science? Dan is dit de juiste cursus.
  • Advanced Data Science with Python – Leer als een pro je modellen te produceren en Python te gebruiken voor machine learning.
Footnotes

0: In deze blog heb ik setuptools
gebruikt om mijn voorbeeldproject op te zetten. Als alternatief
kunt u ook distutils gebruiken,
dat is de standaard tool voor het verpakken in python, maar het mist functies
zoals de find_packages() functie en entry_points.
Omdat het gebruik van setuptools tegenwoordig heel gewoon is en veel van zijn functies
bijzonder nuttig kunnen zijn, stel ik voor dat u setuptools gebruikt.

1: Als u wilt dat de versie van uw pakket ook in python beschikbaar is,
kijk dan hier.

2: U zou uw pakketten ook handmatig kunnen inventariseren, maar dit is bijzonder foutgevoelig.

3: Als alternatief zou u python setup.py install kunnen uitvoeren, maar het gebruik van pip heeft
veel voordelen, waaronder de automatische installatie van afhankelijkheden en de
mogelijkheid om uw pakket te verwijderen of te updaten.

4: U zou ook het scripts argument kunnen gebruiken (zie voor
voorbeeld hier)
maar aangezien dit vereist dat u een python shell script maakt, werkt het misschien niet
zo goed (of helemaal niet) op Windows.

Geef een antwoord

Het e-mailadres wordt niet gepubliceerd.