English
When you pip install a package that exposes console entry points on Windows, pip (via its bundled distlib) produces small native .exe launcher files in the Scripts (or Scripts\) directory. Those .exe files are merely launchers that invoke the target Python interpreter and run your package’s entry point. You can reproduce that same functionality directly from Python by calling distlib.scripts.ScriptMaker — in other words, the .exe-creation logic is available as a callable library.
Below is a compact guide and example code you can drop into a script to generate the same Windows EXE wrappers pip would create.
-
The launcher creation logic lives in
distlib(andpipvendors a copy aspip._vendor.distlib). -
You can call
distlib.scripts.ScriptMakerdirectly to create both POSIX scripts (with shebangs) and Windows.exelaunchers. -
Typical workflow:
pip install distlib(or usepip._vendor.distlib— not recommended for production).- Use
ScriptMakerwithtarget_dirset to your virtualenv’sScriptsfolder (or any folder you prefer). - Call
make()(ormake_multiple()) with either a path to a script file or an entry-spec string likecmdname=module:callable.
-
Caveat: the generated
.exeusually embeds an absolute path to the Python executable used when creating it — so it’s not automatically portable across different environments.
pip install distlib(Or if you intentionally want the exact implementation pip uses, you can import pip._vendor.distlib, but that is using an internal vendored copy and may change without notice.)
# create_launchers.py
from distlib.scripts import ScriptMaker
import os
# Example settings — adjust these paths to your environment
venv_scripts = r"C:\path\to\out" # where to put the generated launchers
source_dir = r"C:\path\to\project" # where source files live (optional)
maker = ScriptMaker(source_dir=source_dir, target_dir=venv_scripts)
# Optional: explicitly set the python executable the launcher should use.
# If omitted, distlib will detect the interpreter running this script.
maker.executable = r"C:\path\to\your\venv\Scripts\python.exe"
# 1) Make a launcher that points to an entry-point style spec:
# format: "cmdname=package.module:callable"
maker.make('mytool=yourpkg.cli:main') # produces mytool.exe and mytool-script.py (on Windows)
# 2) Make a launcher from an actual script file (copy + wrap)
# If you have a script file at source_dir\scripts\run_tool.py:
maker.make(os.path.join('scripts', 'run_tool.py'))
# 3) Make multiple at once:
maker.make_multiple([
'tool1=yourpkg.tool1:main',
'tool2=yourpkg.tool2:run'
])Run this script (with the interpreter you want baked into the launchers). After running, check the venv_scripts folder: you should see mytool.exe (Windows native launcher) and a small Python wrapper script (used by platforms that prefer scripts).
- A path to an actual Python script file (it will copy that and create the appropriate wrappers).
- An entry specification string of the form
name=module:callable(same format asconsole_scriptsentry points).ScriptMakerwill generate a small loader that imports that callable and executes it when invoked.
-
Interpreter path is embedded Generated
.exelaunchers commonly contain the absolute path to the Python interpreter they were created with. If you move the.exeto another machine or virtualenv, it may fail because it looks for the original python path. -
pip._vendor.distlibvsdistlibpipvendorsdistlibinternally and uses it to create launchers during installation. You can importpip._vendor.distlib.scripts.ScriptMaker, but that relies on pip’s private API. Prefer installing the publicdistlibpackage where possible.
-
Permissions / UAC / Antivirus Writing
.exefiles into system or protected locations may trigger UAC or antivirus heuristics. Write to a virtualenvScriptsdir or a user-writable directory. -
Windows vs POSIX differences On POSIX systems
distlibwrites scripts with shebang lines; on Windows it writes a native.exelauncher (plus a.pyfallback wrapper). The sameScriptMakerAPI handles both. -
Not a bundler These
.exefiles are simple launchers. They do not bundle Python or your package’s dependencies. If you need a single-file executable containing the interpreter and all code, consider PyInstaller / cx_Freeze / Nuitka instead.
- pip / pipx:
pipusesdistlibunder the hood to produce launchers.pipxis useful if you specifically want to install isolated CLI tools into per-tool environments and get launchers placed on PATH. - setuptools / entry points: When packaging, declare
console_scriptsinsetup.cfg/setup.py/pyproject.toml.pipwill create the launchers for you on install. - PyInstaller / cx_Freeze / Nuitka: use these if you want a self-contained executable (these bundle the interpreter & dependencies; behavior and use-case differ from
distlib-style launchers).
- If your goal is to reproduce exactly what
pipdoes at install-time (i.e., create small native launchers that call the interpreter and your entry point), usedistlib.scripts.ScriptMakerfrom the officialdistlibpackage. - If you want to automate creation of launchers as part of a packaging or deployment script, call
ScriptMakerfrom within the same interpreter you want the launcher to target and write into that environment’sScriptsdirectory. - If you need portability across different systems or environments, consider alternatives that embed the interpreter (PyInstaller) or create a shift in how you distribute (containerize, shipping a wheel and using
pipon the target).
python -c "from distlib.scripts import ScriptMaker; ScriptMaker(target_dir='C:\\venv\\Scripts').make('hello=hello_pkg.cli:main')"This single command will create hello.exe in C:\venv\Scripts that launches hello_pkg.cli:main using the Python interpreter running the -c command.
日本語
Windows でコンソールエントリポイントを公開するパッケージを pip install すると、pip(内部で同梱している distlib を使用)が Scripts(または Scripts\)ディレクトリに小さなネイティブ .exe ランチャーファイルを生成します。
これらの .exe は単なるランチャーであり、対象の Python インタプリタを起動して、パッケージのエントリポイントを実行するだけのものです。
この仕組みは、Python から distlib.scripts.ScriptMaker を呼び出すことで同じ機能を直接再現できます。つまり、.exe を作成するロジックはライブラリとして利用可能です。
以下は、pip が作成するのと同等の Windows 用 EXE ラッパーを生成するための簡潔なガイドとサンプルコードです。
-
ランチャー作成ロジックは
distlibにあります(pipはpip._vendor.distlibとして同梱)。 -
distlib.scripts.ScriptMakerを直接呼び出すことで、POSIX 用スクリプト(shebang 付き)と Windows 用.exeランチャーの両方を生成できます。 -
一般的なワークフロー:
pip install distlibを実行(または非推奨だがpip._vendor.distlibを使用)。target_dirに仮想環境のScriptsフォルダ(または任意の出力先)を指定してScriptMakerを作成。- スクリプトのパス、または
cmdname=module:callable形式のエントリ指定を使ってmake()(またはmake_multiple())を呼び出す。
-
注意点:生成される
.exeには、作成時に使用した Python 実行ファイルの絶対パスが埋め込まれるのが一般的です。そのため、別環境にそのまま移動しても動作するとは限りません。
pip install distlib(pip が内部で使っている実装と完全に同じものを使いたい場合は pip._vendor.distlib を import することもできますが、これは内部実装に依存するため、本番用途では推奨されません。)
# create_launchers.py
from distlib.scripts import ScriptMaker
import os
# 例の設定 — 自分の環境に合わせてパスを調整してください
venv_scripts = r"C:\path\to\out" # 生成したランチャーの出力先
source_dir = r"C:\path\to\project" # ソースファイルの場所(任意)
maker = ScriptMaker(source_dir=source_dir, target_dir=venv_scripts)
# オプション:ランチャーが使用する Python 実行ファイルを明示的に指定
# 省略した場合、distlib はこのスクリプトを実行している Python を検出します
maker.executable = r"C:\path\to\your\venv\Scripts\python.exe"
# 1) エントリポイント形式の指定からランチャーを作成
# 形式: "cmdname=package.module:callable"
maker.make('mytool=yourpkg.cli:main') # Windows では mytool.exe と mytool-script.py が生成される
# 2) 実際のスクリプトファイルからランチャーを作成(コピー+ラップ)
# source_dir\scripts\run_tool.py が存在する場合:
maker.make(os.path.join('scripts', 'run_tool.py'))
# 3) 複数を一括で作成
maker.make_multiple([
'tool1=yourpkg.tool1:main',
'tool2=yourpkg.tool2:run'
])このスクリプトを、ランチャーに埋め込みたい Python インタプリタで実行してください。
実行後、venv_scripts フォルダを確認すると、mytool.exe(Windows ネイティブランチャー)と小さな Python ラッパースクリプトが生成されているはずです。
- 実在する Python スクリプトファイルへのパス(コピーして適切なラッパーを作成)。
name=module:callable形式のエントリ指定文字列(console_scriptsエントリポイントと同じ形式)。 この場合、呼び出されたときにその callable を import して実行する小さなローダーが生成されます。
-
インタプリタのパスが埋め込まれる 生成された
.exeには、作成時に使用した Python インタプリタの絶対パスが含まれることが一般的です。別のマシンや仮想環境に移動すると、そのパスが見つからず失敗する可能性があります。 -
pip._vendor.distlibとdistlibの違いpipは内部でdistlibを同梱し、インストール時のランチャー生成に使用しています。pip._vendor.distlib.scripts.ScriptMakerを import することも可能ですが、これはpipの非公開 API に依存します。可能であれば、公開パッケージであるdistlibを使用してください。
-
権限 / UAC / アンチウイルス システムや保護された場所に
.exeを書き込むと、UAC やアンチウイルスの検知が発生することがあります。仮想環境のScriptsディレクトリや、ユーザー書き込み可能な場所を使用してください。 -
Windows と POSIX の違い POSIX 系では shebang 付きスクリプトが生成され、Windows ではネイティブ
.exeランチャー(+.pyのフォールバックラッパー)が生成されます。どちらも同じScriptMakerAPI で扱えます。 -
バンドラーではない これらの
.exeは単なるランチャーであり、Python 本体や依存関係を同梱しません。 インタプリタや依存関係を含めた単一実行ファイルが必要な場合は、PyInstaller / cx_Freeze / Nuitka などを検討してください。
- pip / pipx:
pipは内部でdistlibを使ってランチャーを生成します。pipxは CLI ツールを個別の仮想環境に隔離してインストールし、PATH 上にランチャーを配置したい場合に便利です。 - setuptools / entry points: パッケージング時に
setup.cfg/setup.py/pyproject.tomlでconsole_scriptsを定義すれば、pip install時に自動的にランチャーが作成されます。 - PyInstaller / cx_Freeze / Nuitka: Python 本体と依存関係を含む自己完結型の実行ファイルを作りたい場合に使用します(
distlib方式とは用途が異なります)。
pipがインストール時に行うのと同じこと(インタプリタとエントリポイントを呼び出す小さなネイティブランチャーの作成)を再現したい場合は、公式のdistlibパッケージに含まれるdistlib.scripts.ScriptMakerを使用してください。- パッケージングやデプロイ用スクリプトの一部としてランチャー生成を自動化したい場合は、対象とするインタプリタで
ScriptMakerを呼び出し、その環境のScriptsディレクトリに出力してください。 - 環境間での可搬性が必要な場合は、インタプリタを同梱する手法(PyInstaller など)や、配布方法の見直し(wheel 配布+
pip利用、コンテナ化など)を検討してください。
python -c "from distlib.scripts import ScriptMaker; ScriptMaker(target_dir='C:\\venv\\Scripts').make('hello=hello_pkg.cli:main')"この 1 行コマンドで、C:\venv\Scripts に hello.exe が作成され、-c を実行した Python インタプリタを使って hello_pkg.cli:main が起動されるようになります。