Python Virtual Environment Usage on Raspberry Pi
Usage With sudo
Now things are getting more Raspberry Pi specific. Sometimes elevated privileges are needed to access certain hardware on the Raspberry Pi (ex: /dev/mem). The typical way to do this is by invoking the Python script using sudo. However,sudo usage with virtual environments requires some special attention.
It's not as simple assudo make me a sandwich.
NeoPixel Example
Let's use a simple NeoPixel script as an example, since NeoPixels generally require running with sudo. Here's the simple NeoPixel code we want to run, which we will callneo_test.py:
import boardimport neopixelpixels = neopixel.NeoPixel(board.D18, 10)pixels.fill(0xADAF00)
import boardimport neopixelpixels = neopixel.NeoPixel(board.D18, 10)pixels.fill(0xADAF00)
We start with a Raspberry Pi setup with Blinka:
For this example, Blinka is installed into a venv called blinka (remember venv names are just names) using themanual process.
pi@raspberrypi:~ $ python3 -m venv blinka(blinka) pi@raspberrypi:~ $ source blinka/bin/activate(blinka) pi@raspberrypi:~ $ pip3 install --upgrade adafruit-blinka
pi@raspberrypi:~ $ python3 -m venv blinka(blinka) pi@raspberrypi:~ $ source blinka/bin/activate(blinka) pi@raspberrypi:~ $ pip3 install --upgrade adafruit-blinka
And then the NeoPixel library is also installed:
(blinka) pi@raspberrypi:~ $ pip3 install adafruit-circuitpython-neopixel
(blinka) pi@raspberrypi:~ $ pip3 install adafruit-circuitpython-neopixel
If we try runningneo_test.py without sudo, we get a permission error:
(blinka) pi@raspberrypi:~ $ python3 neo_test.py Can't open /dev/mem: Permission deniedTraceback (most recent call last): File "/home/pi/neo_test.py", line 6, in <module> pixels.fill(0xADAF00) File "/home/pi/blinka/lib/python3.11/site-packages/adafruit_pixelbuf.py", line 216, in fill self.show() File "/home/pi/blinka/lib/python3.11/site-packages/adafruit_pixelbuf.py", line 204, in show return self._transmit(self._post_brightness_buffer) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/pi/blinka/lib/python3.11/site-packages/neopixel.py", line 180, in _transmit neopixel_write(self.pin, buffer) File "/home/pi/blinka/lib/python3.11/site-packages/neopixel_write.py", line 42, in neopixel_write return _neopixel.neopixel_write(gpio, buf) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/pi/blinka/lib/python3.11/site-packages/adafruit_blinka/microcontroller/bcm283x/neopixel.py", line 78, in neopixel_write raise RuntimeError(RuntimeError: NeoPixel support requires running with sudo, please try again!swig/python detected a memory leak of type 'ws2811_t *', no destructor found.(blinka) pi@raspberrypi:~ $
(blinka) pi@raspberrypi:~ $ python3 neo_test.py Can't open /dev/mem: Permission deniedTraceback (most recent call last): File "/home/pi/neo_test.py", line 6, in <module> pixels.fill(0xADAF00) File "/home/pi/blinka/lib/python3.11/site-packages/adafruit_pixelbuf.py", line 216, in fill self.show() File "/home/pi/blinka/lib/python3.11/site-packages/adafruit_pixelbuf.py", line 204, in show return self._transmit(self._post_brightness_buffer) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/pi/blinka/lib/python3.11/site-packages/neopixel.py", line 180, in _transmit neopixel_write(self.pin, buffer) File "/home/pi/blinka/lib/python3.11/site-packages/neopixel_write.py", line 42, in neopixel_write return _neopixel.neopixel_write(gpio, buf) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/pi/blinka/lib/python3.11/site-packages/adafruit_blinka/microcontroller/bcm283x/neopixel.py", line 78, in neopixel_write raise RuntimeError(RuntimeError: NeoPixel support requires running with sudo, please try again!swig/python detected a memory leak of type 'ws2811_t *', no destructor found.(blinka) pi@raspberrypi:~ $
The error message says to run with sudo, so let's try that:
(blinka) pi@raspberrypi:~ $ sudo python3 neo_test.py Traceback (most recent call last): File "/home/pi/neo_test.py", line 1, in <module> import boardModuleNotFoundError: No module named 'board'(blinka) pi@raspberrypi:~ $
(blinka) pi@raspberrypi:~ $ sudo python3 neo_test.py Traceback (most recent call last): File "/home/pi/neo_test.py", line 1, in <module> import boardModuleNotFoundError: No module named 'board'(blinka) pi@raspberrypi:~ $
That just throws a different error. And it is confusing. Now it can't find board? What's going on?
The general issue is that sudo launches a new environment when invoked. So the script is running in that new environment, which generally does not known about the virtual environment. That's why the board module (which is part of Blinka installed in the venv) can not be found.
So how do we get around this?
Option 1 - Invoke with sudo passing environment
Thesudo
command has the appealing-E
option which allows users to "preserve the existing environment variables". Since most of the magic with a venv comes from its altering of thePATH
environment variable, it seems like this should work. But it doesn't:
(blinka) pi@raspberrypi:~ $ sudo -E python3 neo_test.pyTraceback (most recent call last): File "/home/pi/neo_test.py", line 1, in <module> import boardModuleNotFoundError: No module named 'board'(blinka) pi@raspberrypi:~ $
(blinka) pi@raspberrypi:~ $ sudo -E python3 neo_test.pyTraceback (most recent call last): File "/home/pi/neo_test.py", line 1, in <module> import boardModuleNotFoundError: No module named 'board'(blinka) pi@raspberrypi:~ $
The-E
did not preserve everything. As a result, the system level Python install ends up being used:
(blinka) pi@raspberrypi:~ $ sudo which python3/usr/bin/python3(blinka) pi@raspberrypi:~ $ sudo -E which python3/usr/bin/python3
(blinka) pi@raspberrypi:~ $ sudo which python3/usr/bin/python3(blinka) pi@raspberrypi:~ $ sudo -E which python3/usr/bin/python3
To get around this, theenv
command can be use inline to allow specifying thePATH
variable be explicitly copied into the invoking environment:
(blinka) pi@raspberrypi:~ $ sudo -E env PATH=$PATH which python3/home/pi/blinka/bin/python3
(blinka) pi@raspberrypi:~ $ sudo -E env PATH=$PATH which python3/home/pi/blinka/bin/python3
Now it's finding the Python in the venv path. Using this to run the script works:
(blinka) pi@raspberrypi:~ $ sudo -E env PATH=$PATH python3 neo_test.py(blinka) pi@raspberrypi:~ $
(blinka) pi@raspberrypi:~ $ sudo -E env PATH=$PATH python3 neo_test.py(blinka) pi@raspberrypi:~ $
Make an Alias
The commandsudo -E env PATH=$PATH python3
is a bit klunky. To make invoking this easier, an alias can be used.
alias supy='sudo -E env PATH=$PATH python3'
alias supy='sudo -E env PATH=$PATH python3'
The namesupy
can be changed to whatever you want. Adding it to.bashrc or.bashrc_aliases will make the alias available with every login. Once the alias is set, can then use it:
Option 2 - Use absolute paths
As mentioned previously, a venv can be usedwithout activating by usingabsolute paths to point to the venv's Python install. This also works with sudo.
An easy way to get the absolute path is to activate the venv and check that way:
pi@raspberrypi:~ $ source blinka/bin/activate(blinka) pi@raspberrypi:~ $ which python3/home/pi/blinka/bin/python3
pi@raspberrypi:~ $ source blinka/bin/activate(blinka) pi@raspberrypi:~ $ which python3/home/pi/blinka/bin/python3
And then all that is needed is to invoke with sudo using that absolute path. To demonstrate, firstdeactivate the venv:
(blinka) pi@raspberrypi:~ $ deactivatepi@raspberrypi:~ $
(blinka) pi@raspberrypi:~ $ deactivatepi@raspberrypi:~ $
Now use sudo and the absolute path,no extra parameters are needed:
pi@raspberrypi:~ $ sudo /home/pi/blinka/bin/python3 neo_test.pypi@raspberrypi:~ $
pi@raspberrypi:~ $ sudo /home/pi/blinka/bin/python3 neo_test.pypi@raspberrypi:~ $
alias supy='sudo /home/pi/blinka/bin/python3'
alias supy='sudo /home/pi/blinka/bin/python3'
Keep in mind the virtual environment is baked into the absolute path. So this command invokes with that specific virtual environment.
But once set, the alias can be used:
pi@raspberrypi:~ $ supy neo_test.pypi@raspberrypi:~ $
pi@raspberrypi:~ $ supy neo_test.pypi@raspberrypi:~ $
Note that the virtual environment is not active in the above example.
Page last edited March 08, 2024
Text editor powered bytinymce.