ShivでPythonアプリをパッケージ化する方法と実装手順

Published on: | Last updated:

最近、Pythonで書いたちょっとしたツールとかスクリプトを、プログラミングやってない同僚とかに渡すとき、どうするのが一番いいんだろうって考えてたんだよね。正直、毎回「まずPythonをインストールして、pipでこのライブラリを入れて…」って説明するの、めちゃくちゃ面倒じゃない?

で、よく聞かれるのが「PyInstallerでexeファイルにしちゃえば?」って話。うん、それも一つの手。でもね、もっと軽くて、なんていうか…もっと「Pythonらしい」やり方があるのを知って、それが今日のテーマの「Shiv」なんだ。

正直、最初は「また新しいツールか…」って思ったんだけど、使ってみたらこれがなかなか良くて。特に、サーバー上で動かすような内部ツールとか、ちょっとした自動化スクリプトをサクッと配布したいときには、PyInstallerより便利な場面が結構あることに気づいたんだ。

で、Shivって結局なんなの?

一言でいうと、ShivはPythonプロジェクトを依存ライブラリもろとも、一つの実行可能なzipファイル(.pyzっていう拡張子になる)に固めてくれるツール。ここがポイントで、PyInstallerみたいにPythonの実行環境そのものをまるごとパッケージングするんじゃなくて、あくまでコードとライブラリだけをまとめる。

だから、出来上がるファイルがすごく軽い。その代わり、動かす環境には互換性のあるPythonがインストールされてる必要があるんだけどね。でも、開発者向けのツールとかなら、大体Python入ってるし、問題ないことが多い。

この.pyzっていうのは、Pythonが公式にサポートしてる「zipapp」っていう形式で、Pythonインタプリタが直接実行できるzipファイルのこと。知ってた?僕はこれで初めて知ったんだけど、なかなか賢い仕組みだよね。

もちろん、どんなプロジェクトでもそのまま.pyzにできるわけじゃなくて、ちょっとだけお作法がある。最低限、こんな感じの構成になってると話が早い。

  • myapp/ (プロジェクトのルート)
    • myapp/ (こっちがPythonのパッケージ本体)
      • __init__.py (これがないとパッケージとして認識されない、空でもOK)
      • __main__.py (コマンドを叩いた時に最初に実行されるファイル)
    • setup.py (プロジェクトの情報とか、実行する関数を定義するところ)
    • requirements.txt (使ってるライブラリの一覧、まあ、これは任意)

要するに、Shivに「このプロジェクトを実行するってなったら、どのファイルのどの関数を呼べばいいの?」ってのを教えてあげる必要があるってこと。そのためにsetup.pyがめちゃくちゃ大事になってくるんだ。

ShivがPythonプロジェクトを一つの実行可能アーカイブにまとめる概念図
ShivがPythonプロジェクトを一つの実行可能アーカイブにまとめる概念図

じゃあ、実際にどうやって作るの?

じゃあ、手を動かしてみよう。思ってるより簡単だから。プロジェクトの準備ができてるなら、ほんの数ステップで終わる。

まず一番大事なのが、さっきも言ったsetup.py。ここで「エントリーポイント」っていうのを定義する。要は、このプログラムを実行するときのコマンド名と、それに対応する関数を結びつける作業だね。

# setup.py の中身

from <a href="https://packaging.python.org/en/latest/tutorials/packaging-projects/" target="_blank" class="blogHightLight_css nobox">setuptools</a> import setup, find_packages

setup(
    name="myapp",
    version="0.1",
    packages=find_packages(),
    entry_points={
        'console_scripts': [
            'myapp = myapp.__main__:main'
        ]
    },
    install_requires=[
        'requests', # 例えばrequestsライブラリに依存してるとか
        'beautifulsoup4',
    ],
)

この'myapp = myapp.__main__:main'っていう部分がキモ。myappっていうコマンドを叩いたら、myappパッケージの中の__main__.pyファイルにあるmain()関数を実行してね、っていう意味。コロン(:)でモジュールと関数を区切るのがsetuptoolsの書き方なんだ。

で、もちろん、呼び出される側の__main__.pyもちゃんと用意しないとダメ。

# myapp/__main__.py の中身

def main():
    print("Shivでパッケージ化されたアプリへようこそ!")
    # ここに実際の処理を書く

ここまでできたら、あとはターミナルでShivコマンドを一発叩くだけ。

# カレントディレクトリをパッケージングする場合
shiv -c myapp -o myapp.pyz .

それぞれのオプションの意味はこんな感じ。

  • -c myapp:実行するコンソールスクリプトを指定。setup.pyで定義したやつね。
  • -o myapp.pyz:出力するファイル名。まあ、これは見ての通り。
  • .:パッケージ化する対象のディレクトリ。この場合はカレントディレクトリ。

もしrequirements.txtで依存関係を管理してるなら、それを使うこともできる。こっちのほうがsetup.pyがスッキリして個人的には好きかな。

# requirements.txt を使う場合
shiv -c myapp -o myapp.pyz -r requirements.txt .

これでmyapp.pyzっていうファイルができあがる。あとはこれを実行するだけ。Unix系(MacとかLinux)なら実行権限をつけて、

chmod +x myapp.pyz
./myapp.pyz

Windowsなら、

python myapp.pyz

で動く。簡単でしょ?これで仮想環境がどうとか、pip installがどうとか言わずに済む。

開発環境でのShivコマンド実行の様子
開発環境でのShivコマンド実行の様子

PyInstallerじゃダメなの?Shivとどっちがいい?

ここで、みんなが思う疑問。「で、結局PyInstallerとどっちがいいの?」って話。これはもう、完全に「場合による」としか言えないんだけど、判断するための比較表を作ってみた。僕の個人的な感想も入ってるけどね。

日本ではPyInstallerがすごく人気で、ググれば日本語の情報がたくさん出てくるよね。でも、ShivはもともとLinkedInが社内ツールを配布するために作ったもので、そういう「開発者が開発者のために」使うシナリオにすごく強い。海外のテック企業のこういう動きを見てると、PyInstallerとは違う設計思想があるのがわかって面白い。公式のGitHubリポジトリを見ると、その背景がよくわかるよ。

比較ポイント Shiv (.pyz) PyInstaller (.exe / バイナリ)
配布物のサイズ めっちゃ軽い。コードと依存関係だけだから。数十KBとか数百KBで済むことも。 重い…。Pythonインタプリタごとだからね、仕方ないけど。簡単なものでも数十MBになるのはザラ。
実行環境 Pythonのインストールが必須。バージョン互換性も気にする必要あり。 Python不要。完全に自己完結してるから、誰にでも渡せる。これが最大の強み。
ビルド時間 一瞬。ほんとに。ファイルをzipに固めてるだけに近いから。 まあまあ時間かかる。依存関係を解析して、実行ファイルを構築するから、プロジェクトが大きくなるとコーヒー一杯飲めるくらい待つ。
ユースケース 開発チーム内のツール配布、CI/CDでの利用、サーバー上のスクリプト実行とか。 エンドユーザー(非開発者)向けのデスクトップアプリ配布。とにかくダブルクリックで動いてほしい場合。
Windows対応 .pyzはネイティブじゃないから、python myapp.pyzって一手間いる。まあ、ラッパー作ればいいんだけど。 完璧。.exeが作れるから、Windowsユーザーには一番親切。

要するに、渡す相手がPCに詳しくて、Python環境がある程度整ってるならShivのほうが手軽で速い。逆に、PCに詳しくない人に「このファイル、ダブルクリックして」で終わらせたいならPyInstaller一択、って感じかな。トレードオフだね。

Windowsユーザー向けにexeっぽく見せる方法

さっきの表でもちょっと触れたけど、「Shivはいいけど、Windowsユーザーにpython myapp.pyzって打ってもらうのはちょっと…」って思うよね。わかる。でも、これもちゃんと解決策がある。

一番簡単なのは、バッチファイル(.bat)を一緒に渡す方法。メモ帳でこんなファイルを作るだけ。

REM launch_myapp.bat
@echo off
python myapp.pyz %*

このlaunch_myapp.batmyapp.pyzを同じフォルダに入れておけば、ユーザーはバッチファイルをダブルクリックするだけで実行できる。うん、これで十分なことも多い。

もうちょっとスマートにやりたいなら、PyInstallerを「ラッパー」として使うっていう合わせ技もある。まず、こんな感じの超短いPythonスクリプト(例えば launcher.py)を作る。

# launcher.py
import runpy
runpy.run_path("myapp.pyz", run_name="__main__")

やってることは、ただmyapp.pyzをPythonで実行してるだけ。で、このlauncher.pyをPyInstallerで固める。

pyinstaller --onefile launcher.py

そうするとdistフォルダにlauncher.exeができる。これをmyapp.exeとかに名前を変えて、myapp.pyzと一緒に配布すれば、ユーザーは.exeをダブルクリックするだけで、実質的にShivのパッケージが動く、っていう仕組み。ちょっとトリッキーだけど、見た目は完全にネイティブアプリになる。

pyzファイルと、exeラッパーで包んだ後の見た目の比較
pyzファイルと、exeラッパーで包んだ後の見た目の比較

これ、よくハマるから気をつけて

最後に、僕がShivを使い始めて「うわっ」ってなった、よくある失敗例をいくつか共有しておく。これを知ってれば、無駄な時間を溶かさずに済むはず。

  • entry_pointsのタイプミスsetup.py'myapp = myapp.__main__:main'の部分、一文字でも間違ってると「そんなコマンドないよ」って言われて動かない。特にファイルパスとか関数名を間違えやすい。ビルドは成功しちゃうからタチが悪いんだよね。
  • __init__.pyの存在を忘れる:サブディレクトリに処理を分けてる場合とか、各ディレクトリに__init__.pyファイルを置かないと、Pythonがそれをパッケージとして認識してくれない。結果、ImportErrorになる。基本だけど、つい忘れがち。
  • パッケージ化するパスの間違いshiv ... .の最後の「.」を忘れたり、違うディレクトリで実行したりすると、意図しないファイル構成になって実行時に「ファイルが見つかりません」ってなる。ちゃんとプロジェクトルートで実行するのが大事。
  • 大事な情報をハードコーディングしちゃう:これはShivに限った話じゃないけど、配布が簡単になる分、うっかりAPIキーとかパスワードをコードに直接書いたままパッケージ化しちゃうと大事故になる。Shivはただのスーツケース。金庫じゃないからね。大事な情報は環境変数とか、別の設定ファイルから読み込むようにしよう。本当に。

まあ、だいたいこんな感じかな。Shivは、Pythonのエコシステムにうまく乗っかった、すごくクレバーなツールだと思う。全部をexeにするんじゃなくて、こういう選択肢があるって知っておくだけでも、Pythonでの開発がまた一つ面白くなるんじゃないかな。

あなたが今作ってるツールなら、ShivとPyInstaller、どっちのほうが向いてると思う? もしよかったら、理由も一緒にコメントで教えてくれると嬉しいな!

Related to this topic:

Comments

  1. Guest 2025-09-07 Reply
    子供のプログラミング、大切だよね。うちの子も最近コーディングに興味出てきて。Shivって聞いたことないけど、セキュリティとか考えると、こういうツール知っておくのはいいかも。プログラミングって将来役立つよね~。
  2. Guest 2025-04-17 Reply
    このガイドは興味深いですね。Shivの使い方についてもう少し具体的な例があると、理解が深まりそうです。特にアーカイブのビルド手順に関して、実際の流れを示してもらえると助かります!