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 as sudo 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 call neo_test.py:
import board import neopixel pixels = 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 the manual process.
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
If we try running neo_test.py without sudo, we get a permission error:
(blinka) pi@raspberrypi:~ $ python3 neo_test.py Can't open /dev/mem: Permission denied Traceback (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 board ModuleNotFoundError: 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
The sudo
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 the PATH
environment variable, it seems like this should work. But it doesn't:
(blinka) pi@raspberrypi:~ $ sudo -E python3 neo_test.py Traceback (most recent call last): File "/home/pi/neo_test.py", line 1, in <module> import board ModuleNotFoundError: 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
To get around this, the env
command can be use inline to allow specifying the PATH
variable be explicitly copied into the invoking environment:
(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:~ $
Make an Alias
The command sudo -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'
The name supy
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:
(blinka) pi@raspberrypi:~ $ supy neo_test.py
Option 2 - Use absolute paths
As mentioned previously, a venv can be used without activating by using absolute 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
And then all that is needed is to invoke with sudo using that absolute path. To demonstrate, first deactivate the venv:
(blinka) pi@raspberrypi:~ $ deactivate pi@raspberrypi:~ $
Now use sudo and the absolute path, no extra parameters are needed:
pi@raspberrypi:~ $ sudo /home/pi/blinka/bin/python3 neo_test.py pi@raspberrypi:~ $
Make an Alias
An alias can be made for this as well:
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.py pi@raspberrypi:~ $
Note that the virtual environment is not active in the above example.
Text editor powered by tinymce.