💮

【Raspberry Pi/Python】venv(仮想環境)でRPi.GPIOを使う

2024/11/02に公開

RPi.GPIOモジュールと仮想環境

Raspberry Piが有する特徴の一つとして、GPIO (General Purpose Input Output)の存在がある。多くの場合、これをプログラムで制御することになる。Pythonならば、Raspberry Piに標準で搭載されているRPi.GPIOを使うことができる。

仮想環境の需要と概要

輓近のPythonでは、方針転換を以て、pipの使用に制限が加わった。

pip install error
$ pip install TkEasyGUI
error: externally-managed-environment

× This environment is externally managed
╰─> To install Python packages system-wide, try apt install
    python3-xyz, where xyz is the package you are trying to
    install.
    
    If you wish to install a non-Debian-packaged Python package,
    create a virtual environment using python3 -m venv path/to/venv.
    Then use path/to/venv/bin/python and path/to/venv/bin/pip. Make
    sure you have python3-full installed.
    
    For more information visit http://rptl.io/venv

note: If you believe this is a mistake, please contact your Python installation or OS distribution provider. You can override this, at the risk of breaking your Python installation or OS, by passing --break-system-packages.
hint: See PEP 668 for the detailed specification.

標準のPythonは、全てのユーザーが等しく使えるものである。従って、pipでのインストール、アンインストール、バージョンアップ・ダウンもまた、全てのユーザーに影響する。これを是とせず、このような告諭が憑き纏うようになったのである。

回避策は二つある。

  1. --break-system-packages
  2. 仮想環境

--break-system-packages

先のメッセージを見ると、斯様な記述がある。

抜粋
You can override this, at the risk of breaking your Python installation or OS, by passing --break-system-packages.

要するに、折角の配慮に無頓着ならば、--break-system-packagesを付して強行すればよい。

従来通り、全ユーザーに影響するインストール
pip install TkEasyGUI --break-system-packages

仮想環境

危険を冒さず、穏便に済ませたい場合には、仮想環境を作ることで対処する。

仮想環境作成の基本構文
python -m venv 【命名】

例に、default_venvという名で作るならば、次の通り。

仮想環境作成
$ python -m venv default_venv

作っただけでは意味がないため、仮想環境を有効にする。

仮想環境指定
$ source default_venv/bin/activate

仮想環境の内部にactivateというファイルがあるため、これを指定すればよい。

仮想環境の構造
$ tree -L 2
.
├── bin
│   ├── Activate.ps1
│   ├── activate
│   ├── activate.csh
│   ├── activate.fish
│   ├── pip
│   ├── pip3
│   ├── pip3.11
│   ├── python -> /usr/bin/python
│   ├── python3 -> python
│   ├── python3.11 -> python
│   ├── uv
│   └── uvx
├── include
│   └── python3.11
├── lib
│   └── python3.11
├── lib64 -> lib
└── pyvenv.cfg

7 directories, 13 files
Windowsの場合

Windowsでは、binの代わりにScriptsがある。

仮想環境の構造(一部省略)
PS C:\~中略~\.venv> tree
フォルダー パスの一覧:  ボリューム OS
ボリューム シリアル番号は ~~ です
C:.
├─Lib
│  └─site-packages
│      ├─blinker
│      ├─blinker-1.8.2.dist-info
│      ├─click
│      ├─click-8.1.7.dist-info
│      ├─colorama
│      │  ├─tests
︙
│      ├─zipp-3.20.2.dist-info
│      └─__pycache__
└─Scripts

PS C:\~中略~\.venv> ls .\Scripts\

    Directory: C:\~中略~\.venv\Scripts

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
la---      2024/10/23()    22:05           3798 activate
la---      2024/10/23()    22:05           2386 activate_this.py
la---      2024/10/23()    22:05           2346 activate.bat
la---      2024/10/23()    22:05           2705 activate.csh
la---      2024/10/23()    22:05           4269 activate.fish
la---      2024/10/23()    22:05           3954 activate.nu
la---      2024/10/23()    22:05           2782 activate.ps1
la---      2024/10/23()    22:05           1728 deactivate.bat
la---      2024/10/23()    22:05          43058 flask.exe
la---      2024/10/23()    22:05           1215 pydoc.bat
la---      2022/01/17()    15:30         597904 python.exe
la---      2022/01/17()    15:30         597392 pythonw.exe

仮想環境を有効にする際は、単にactivateを実行すればよい。

activate
PS C:\~中略~\.venv> .\Scripts\activate

仮想環境を有効にすれば、基本的には、先のようなエラーメッセージが現れなくなる。

なお、仮想環境を脱するには、ただdeactivateと入力する。当然乍ら、仮想環境外では、仮想環境にインストールしたものを使うことはできない。

仮想環境にRPi.GPIOがない

defaultの名で新たに仮想環境を作る。

新しい仮想環境
$ python -m venv default
$ source default/bin/activate
(default) $ pip list
Package    Version
---------- -------
pip        23.0.1
setuptools 66.1.1

上の通り、RPi.GPIOどころか何もないため、必要に応じてインストールする。

インストール
(default) $ pip install RPi.GPIO
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Collecting RPi.GPIO
  Using cached RPi.GPIO-0.7.1.tar.gz (29 kB)
  Preparing metadata (setup.py) ... done
Installing collected packages: RPi.GPIO
  DEPRECATION: RPi.GPIO is being installed using the legacy 'setup.py install' method, because it does not have a 'pyproject.toml' and the 'wheel' package is not installed. pip 23.1 will enforce this behaviour change. A possible replacement is to enable the '--use-pep517' option. Discussion can be found at https://github.com/pypa/pip/issues/8559
  Running setup.py install for RPi.GPIO ... done
Successfully installed RPi.GPIO-0.7.1

インストールされたことが確認できる。

確認
(default) $ pip list
Package    Version
---------- -------
pip        23.0.1
RPi.GPIO   0.7.1
setuptools 66.1.1
(default) $ python3
Python 3.11.2 (main, May  2 2024, 11:59:08) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import RPi.GPIO
>>>

しかしRaspberry Piは、RPi.GPIOを含む種々のモジュールを標準に搭載しているため、願わくは有効に活用したいものである。このように、仮想環境外のモジュールを仮想環境内で扱う場合には、作成時に--system-site-packagesを付する。

本題

--system-site-packagesは、新たに仮想環境を作る際のオプションである。

https://zenn.dev/tanny/articles/ce010edd69d56a

新規作成(system_venv)
$ python -m venv system_venv --system-site-packages
$ source system_venv/bin/activate
(system_venv) $ python3
Python 3.11.2 (main, May  2 2024, 11:59:08) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import RPi.GPIO
>>>

このように、仮想環境にインストールせずとも初めから利用できる。

仮想環境問題の発展

以上には、本題に示したものの外にも幾つか手法が見える。これらの選択に定説はなく、全く現在の用途や目的に従うものである。個人利用程度のものであれば、仮想環境を作る手間が勝ることもあろう。

しかし、少しでも本腰を入れた開発であれば、バージョンを始め、何らかの管理から遯逃することは出来ない。複数人での作業においては、バージョン等の不一致による手戻りを防ぐことが期待される。また時には、同じモジュールの異なるバージョンが同時に欲せられることもあり得る。時に枯れた技術の安定性を頼ることも、開発上有効に働く場合がある。そこで、プロジェクト毎に仮想環境を作成することで、「使うもの以外存在しない」単純明快な状態を作ることができる。

uvモジュールと--system-site-packages

uvは、そうした開発上の管理を簡便にするものの一つである。

インストール
(system_venv) $ pip install uv
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Collecting uv
  Downloading uv-0.4.29-py3-none-manylinux_2_28_aarch64.whl (12.8 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 12.8/12.8 MB 3.5 MB/s eta 0:00:00
Installing collected packages: uv
Successfully installed uv-0.4.29

以後、uvというコマンドが有効になるため、pippythonといったコマンドは使わなくなる。なお、これはuvをインストールした仮想環境に限る。

プロジェクトを作る

次のコマンドで、プロジェクトの概形が作られる。

プロジェクト作成の基本構文
uv init 【命名】

led_blinkの名で作成する。

作成例
(system_venv) $ uv init led_blink
Initialized project `led-blink` at `/home/〜中略〜/led_blink`
(system_venv) $ tree led_blink/
led_blink/
├── README.md
├── hello.py
└── pyproject.toml

1 directory, 3 files

プロジェクト専用の仮想環境を作る

uvの特徴として、仮想環境が(やたら)高速に作成される。仮想環境の作成にはしばしば時間を要することがあり、性能の高くない環境であるほどそれは顕著になる。プロジェクトを作る度に仮想環境の作成を待っていたが、それが全く無くなった。
ここでは--system-site-packagesを指定する。また名前を指定しないため、.venvの名で作られる。

仮想環境作成例
(system_venv) $ uv venv --system-site-packages
warning: `VIRTUAL_ENV=/home/〜中略〜/system_venv` does not match the project environment path `.venv` and will be ignored
Using CPython 3.11.2 interpreter at: /usr/bin/python
Creating virtual environment at: .venv
Activate with: source .venv/bin/activate

ここで警告が表示されるが、単なる注意である。

抜粋
warning: `VIRTUAL_ENV=/home/〜中略〜/system_venv` does not match the project environment path `.venv` and will be ignored

今、uvは仮想環境にしかないため、仮想環境を有効にした状態である。一方で、新たに別の仮想環境を作成し、扱おうとしている。
このため、「プログラム実行時には、現在有効の仮想環境が無視される」ことを伝えている。寧ろ、そのような動作を望んでいるため、特段気にする必要はない。

簡単な検証

何方が優先されるかは、動かしてみれば分かる。

対象をTkEasyGUIモジュールとして検証する。
system_venvにはインストールせず、.venvにはインストールする。

system_venvには存在しないことを確認する
(system_venv) $ python3
Python 3.11.2 (main, May  2 2024, 11:59:08) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import TkEasyGUI
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'TkEasyGUI'
>>>

.venvは、uvで作成したプロジェクトに紐づいている。このため、インストールは次の法が容易に済む。

.venvにインストールする
(system_venv) $ uv add TkEasyGUI
warning: `VIRTUAL_ENV=/home/〜中略〜/system_venv` does not match the project environment path `.venv` and will be ignored
Resolved 4 packages in 4.22s
   Built pyperclip==1.9.0
Prepared 3 packages in 3.68s
Installed 3 packages in 13ms
 + pillow==11.0.0
 + pyperclip==1.9.0
 + tkeasygui==0.2.76

プロジェクトとしてプログラムを実行する時、望むのは.venvでの動作である。従って、TkEasyGUIが利用できることを確認すればよい。

import TkEasyGUI as tk
print(tk.__version__)

プログラムの実行は次のように記述する。

(system_venv) $ uv run import_tk.py
warning: `VIRTUAL_ENV=/home/〜中略〜/system_venv` does not match the project environment path `.venv` and will be ignored
0.2.76

TkEasyGUIのバージョンが表示されたため、望む通り、.venvで動作していることが確かめられた。

動作

LEDを接続した上で、点滅さすプログラムを実行したところには、問題なく動作した。

import RPi.GPIO as GPIO
import time

LED_PORT = 17

def setup():
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(LED_PORT, GPIO.OUT)

def loop():
    while True:
        GPIO.output(LED_PORT, GPIO.HIGH)
        time.sleep(1)

        GPIO.output(LED_PORT, GPIO.LOW)
        time.sleep(1)

if __name__ == '__main__':
    try:
        setup()
        loop()
    except KeyboardInterrupt:
        GPIO.cleanup()
        print('\r', end='')
    finally:
        exit(0)

配線
配線図
回路
回路図

そもそもuvは、Rustで作られたものだと言う。仮想環境作成の速さはこれに起因するほか、プロジェクト単位の管理・開発手法は、Rustに於けるcargoによるものと酷似する。これらは煩雑と見えるも親切であり、反対に旧来の手法は簡単と見えるも不親切であったと感ずる。

Discussion