Misschien heeft een stuk software je wel eens om toestemming gevraagd om je PATH-variabele te wijzigen, of vertelden de installatie-instructies van een ander programma op mysterieuze wijze dat je "je LD_LIBRARY_PATH-variabele correct moet instellen".
Als data scientist kun je andere problemen met omgevingsvariabelen tegenkomen wanneer je met je compute stack werkt (vooral als je er niet volledig de controle over hebt, zoals ik). Deze post is bedoeld om te ontrafelen wat een omgevingsvariabele is en hoe die in een data science-context wordt gebruikt.
Wat is een omgevingsvariabele?
Laat me eerst uitleggen wat een omgevingsvariabele is, door dieper in te gaan op de PATH-omgevingsvariabele. Ik moedig je aan om de hier getoonde commando's uit te voeren in je bash-terminal (met passende aanpassingen — lees de tekst om te snappen wat ik doe!).
Wanneer je inlogt op je computersysteem, bijvoorbeeld in de terminal van je lokale computer of op je externe server via SSH, moet je bash-interpreter weten waar specifieke programma's te vinden zijn, zoals nano (de teksteditor), of git (je versiebeheersoftware), of je Python-uitvoerbaar bestand. Dit wordt geregeld door je PATH-variabele. Die specificeert de paden naar mappen waar je uitvoerbare programma's staan.
Volgens historische conventie staan commandlineprogramma's, zoals nano, which en top, in de map /usr/bin. (Volgens historische conventie is de map /bin voor softwarebinaries, vandaar de naam /bin.) Dit zijn de programma's die met je besturingssysteem worden meegeleverd en waarvoor speciale rechten nodig zijn om ze te updaten.
Probeer het in je terminal:
$ which which
/usr/bin/which
$ which top
/usr/bin/top
Andere programma's worden (om wat voor reden dan ook) in /bin geïnstalleerd. ls is daar een voorbeeld van:
$ which ls
/bin/ls
Weer andere programma's kunnen in andere speciale mappen zijn geïnstalleerd:
$ which nano
/usr/local/bin/nano
Hoe weet je Bash-terminal waar die dingen staan? Daarvoor gebruikt hij de omgevingsvariabele PATH. Die ziet er ongeveer zo uit:
$ echo $PATH
/usr/bin:/bin:/usr/local/bin
Het belangrijkste om te onthouden over de PATH-variabele is dat hij "door dubbele punten gescheiden" is. Met andere woorden: elk mappad is gescheiden van het volgende door een "dubbelpunt" (:). De volgorde waarin je bash-terminal naar programma's zoekt, loopt van links naar rechts:
/usr/bin/bin/usr/local/bin
Op mijn computer kijkt mijn bash-interpreter eerst in de map /usr/bin wanneer ik ls intyp. Hij zal zien dat ls niet in /usr/bin bestaat en doorgaan naar de volgende map, /bin. Omdat mijn ls onder /bin staat, wordt het programma ls daarvandaan uitgevoerd.
Je ziet dus dat dit tegelijk superflexibel is om je compute-omgeving aan te passen, maar ook superfrustrerend kan zijn als een programma je PATH-variabele heeft gewijzigd zonder dat je het weet.
Wacht, kun je je PATH-variabele echt wijzigen? Jazeker, en er zijn een paar manieren om dat te doen.
Hoe je de PATH-variabele wijzigt
Met een bash-sessie
De eerste manier is vluchtig, of tijdelijk, en geldt alleen voor je huidige bash-sessie. Je kunt een map hogere prioriteit geven dan de bestaande paden door hem te "voorafgaan" aan de PATH-variabele:
$ export PATH=/path/to/my/folder:$PATH
$ echo $PATH
/path/to/my/folder:/usr/bin:/bin:/usr/local/bin
Of je kunt hem een lagere prioriteit geven dan de bestaande paden door hem aan de PATH-variabele te "toe te voegen" aan het einde:
$ export PATH=$PATH:/path/to/my/folder
$ echo $PATH
/usr/bin:/bin:/usr/local/bin:/path/to/my/folder
Dit is tijdelijk omdat ik hem alleen exporteer tijdens mijn huidige bash-sessie.
bashrc- of .bash_profile-bestand
Als ik mijn wijzigingen wat permanenter wil maken, zet ik ze in mijn .bashrc- of .bash_profile-bestand. (Ik raad aan om het .bashrc-bestand te gebruiken.) Het .bashrc/.bash_profile-bestand staat in je thuismap (je $HOME-omgevingsvariabele geeft dit aan) en is een bestand dat je bash-interpreter als eerste laadt en uitvoert. Hij voert alle commando's daarin uit. Dat betekent dat je je PATH-variabele kunt wijzigen door simpelweg het volgende in je .bashrc te zetten:
...other stuff above...
# Make /path/to/folder have higher priority
export PATH=/path/to/folder:$PATH
# Make /path/to/other/folder have lower priority
export PATH=$PATH:/path/to/folder
...other stuff below...
Data science en de PATH-omgevingsvariabele
Hoe is dit relevant voor data scientists? Nou, als je data scientist bent, is de kans groot dat je Python gebruikt, en dat je Python-interpreter afkomstig is van de Anaconda Python-distributie (echt geweldig, ga 'm halen!). De Anaconda-installer geeft de map /path/to/anaconda/bin prioriteit in de omgevingsvariabele PATH. Je kunt andere Python-interpreters op je systeem hebben geïnstalleerd (Apple levert er bijvoorbeeld zelf een mee). Maar door deze PATH-aanpassing zorg je ervoor dat elke keer dat je python in je Bash-terminal typt, je de Python-interpreter van de Anaconda-distributie gebruikt. In mijn geval ziet mijn PATH er na installatie van Anaconda zo uit:
$ echo $PATH
/Users/ericmjl/anaconda/bin:/usr/bin:/bin:/usr/local/bin
Nog beter: conda-omgevingen plaatsen het pad naar de binaire map van de conda-omgeving vooraan zolang de omgeving is geactiveerd. Bijvoorbeeld: voor mijn blog houd ik die in een omgeving met de naam lektor. Dus...
$ echo $PATH
/Users/ericmjl/anaconda/bin:/usr/bin:/bin:/usr/local/bin
$ which python
/Users/ericmjl/anaconda/bin/python
$ source activate lektor
$ echo $PATH
/Users/ericmjl/anaconda/envs/lektor/bin:/Users/ericmjl/anaconda/bin:/usr/bin:/bin:/usr/local/bin
$ which python
/Users/ericmjl/anaconda/envs/lektor/bin/python
Merk op dat de bash-terminal nu de Python in de lektor-omgeving met hogere prioriteit prefereert.
Als je tot hier bent gekomen, zie je hopelijk een paar belangrijke concepten. Even samenvatten:
PATHis een omgevingsvariabele die als gewone tekststring wordt opgeslagen en door de bash-interpreter wordt gebruikt om te bepalen waar uitvoerbare programma's te vinden zijn.PATHis door dubbele punten gescheiden; mappen met hogere prioriteit staan links in de string, terwijl mappen met lagere prioriteit rechts staan.PATHkan worden gewijzigd door mappen vooraan toe te voegen (prepending) of achteraan toe te voegen (appending) aan de variabele. Dit kan tijdelijk in een bash-sessie door hetexport-commando op de prompt uit te voeren, of permanent over bash-sessies heen door eenexport-regel toe te voegen aan je.bashrcof.bash_profile.
Andere interessante omgevingsvariabelen
Welke andere omgevingsvariabelen kan een data scientist tegenkomen? Dit is een greep eruit die je kunt zien en mogelijk moet fixen, zeker in contexten waar je systeembeheerders op vakantie zijn (of te lang doen over een reactie).
Voor algemeen gebruik wil je zeker weten waar je HOME-map is — op Linux-systemen is dat vaak /home/username, terwijl het op macOS-systemen vaak /Users/username is. Je kunt achterhalen wat HOME is met:
$ echo $HOME
/Users/ericmjl
Als je Python gebruikt is de PYTHONPATH een variabele die handig kan zijn. Die wordt door de Python-interpreter gebruikt en geeft aan waar Python-modules/pakketten te vinden zijn.
Als je met C++-bibliotheken te maken hebt, is je omgevingsvariabele LD_LIBRARY_PATH heel belangrijk. Ik ben hier niet deskundig genoeg in om er zinnig over uit te wijden, dus verwijs ik naar deze website voor meer informatie over best practices voor het gebruik van de variabele LD_LIBRARY_PATH.
Als je met Spark werkt, dan is de omgevingsvariabele PYSPARK_PYTHON interessant. Die vertelt Spark in feite welke Python hij moet gebruiken voor zowel de driver als de workers; je kunt ook PYSPARK_DRIVER_PYTHON los instellen van de omgevingsvariabele PYSPARK_PYTHON, indien nodig.
Hack je omgevingsvariabelen
Hier wordt het leuk! Volg mee voor dingen die je kunt doen door te sleutelen aan je omgevingsvariabelen.
Hack #1: Toegang tot PyPy inschakelen. Ik volg af en toe de ontwikkeling van PyPy, maar omdat PyPy nog niet de standaard Python-interpreter is en nog niet met conda install te installeren is, zet ik het in een eigen map $HOME/pypy/bin. Om toegang tot de PyPy-interpreter te krijgen, moet ik ervoor zorgen dat mijn /path/to/pypy in de omgevingsvariabele PATH staat, maar met een lagere prioriteit dan mijn gewone CPython-interpreter.
Hack #2: Toegang inschakelen tot andere taalinterpreters/compilers. Dit is analoog aan PyPy. Ik heb ooit de JIT-interpreter van Lua uitgeprobeerd om Torch voor deep learning te gebruiken, en moest daarvoor een pad toevoegen in mijn .bashrc.
Hack #3: Python-pakketten in je thuismap installeren. Op gedeelde Linux-computesystemen die het modules-systeem gebruiken in plaats van conda-omgevingen, kan een geladen modulefile zijn geconfigureerd met een virtuele omgeving die je niet mag wijzigen. Als je een Python-pakket moet installeren, kun je pip install --user my_pkg_name gebruiken. Dit installeert het in $HOME/.local/lib/python-[version]/site-packages/. In dit geval is het belangrijk dat je PYTHONPATH $HOME/.local/lib/python-[version]/site-packages met voldoende hoge prioriteit bevat.
Hack 4: Debuggen als het misgaat. Als er een fout optreedt of je onverwacht gedrag ziet — iets wat ik eerder meemaakte was dat mijn Python-interpreter niet goed werd gevonden na het laden van al mijn Linux-modules — dan kun je debuggen door je PATH-omgevingsvariabele tijdelijk op een soort "standaardwaarden" te zetten en die te sourcen, waarmee je je PATH-variabele effectief "reset", zodat je tijdens het debuggen handmatig kunt pre-penden/appenden.
Zet hiervoor de volgende regels in een bestand met de naam .path_default in je thuismap:
export PATH="" # resets PATH to an empty string.
export PATH=/usr/bin:/bin:/usr/local/bin:$PATH # this is a sensible default; customize as needed.
Nadat er iets misgaat, kun je je PATH-omgevingsvariabele resetten met het commando "source":
$ echo $PATH
/some/complicated/path:/more/complicated/paths:/really/complicated/paths
$ source ~/.path_default
$ echo $PATH
/usr/bin:/bin:/usr/local/bin
Let op: je kunt exact dezelfde commando's ook in je bash-sessie uitvoeren; de interactiviteit kan ook helpen.
Conclusie
Ik hoop dat je dit artikel leuk vond en dat het je, eh, een pad vooruit geeft wanneer je deze omgevingsvariabelen tegenkomt!
