pywsl2
A Python wrapper for the Windows Subsystem for Linux API (wslapi.h)
Goal
pywsl2 lets you control WSL distributions and the processes running inside them from Python, without shelling out to wsl.exe and
parsing its UTF-16 output. It calls the native WSL API directly, and its process interface feels like subprocess.
How?
The design follows pydia3:
- Every
wslapi.hfunction is wrapped by a C++ function in WslLib, which handles all handle and memory management (pipes, process handles,CoTaskMemFreed configuration strings) and turns failingHRESULTs into exceptions. - pywsl2 (the native extension) exposes WslLib as C-API Python classes. It never calls the WSL API directly, and it releases the GIL around every blocking call.
wslapi.dllis loaded lazily, soimport pywsl2works on any Windows machine. If WSL is missing, the first API call raisesWslNotAvailableError.
Installation
pywsl2 is Windows-only (x64) and supports CPython 3.12, 3.13, 3.14 and 3.15. WSL must be installed (wsl --install).
pip install pywsl2
Usage
import pywsl2
print(pywsl2.list_distributions()) # [pywsl2.Distribution('Ubuntu'), ...]
ubuntu = pywsl2.default_distribution() # or pywsl2.Distribution("Ubuntu")
# Run a command to completion, like subprocess.run (output is always captured).
result = ubuntu.run("uname -a", text=True, check=True)
print(result.stdout)
# Feed it input; large inputs and outputs are pumped concurrently, so nothing deadlocks.
print(ubuntu.run("sort", input="b\na\n", text=True).stdout)
# Start a process and talk to it.
process = ubuntu.launch(
"read name; echo hello $name", stdin=pywsl2.PIPE, stdout=pywsl2.PIPE
)
process.write(b"world\n")
print(process.read_stdout()) # b'hello world\n'
print(process.wait()) # 0
# Or let communicate() handle the I/O, with an optional timeout.
with ubuntu.launch("cat", stdin=pywsl2.PIPE, stdout=pywsl2.PIPE) as process:
stdout, _ = process.communicate(b"data", timeout=10)
# Attach a command (or, with no command, the default shell) to the current console.
ubuntu.launch_interactive("htop")
# Inspect and change the distribution's configuration.
config = ubuntu.configuration
print(config.version, config.default_uid, config.flags, config.default_environment)
ubuntu.configure(flags=config.flags & ~pywsl2.DistributionFlags.APPEND_NT_PATH)
See Examples/ for more.
API overview
wslapi.h |
pywsl2 |
|---|---|
WslIsDistributionRegistered |
Distribution.is_registered |
WslGetDistributionConfiguration |
Distribution.configuration (and version, default_uid, flags, default_environment) |
WslConfigureDistribution |
Distribution.configure(default_uid=..., flags=...) |
WslLaunch |
Distribution.launch(...) → Process, Distribution.run(...) |
WslLaunchInteractive |
Distribution.launch_interactive(...) |
WslRegisterDistribution |
Distribution.register(name, tar_gz_path) |
WslUnregisterDistribution |
Distribution.unregister() |
(registry, HKCU\...\Lxss) |
list_distributions(), default_distribution() |
Streams passed to launch() are None (inherit this process's stream, the default), PIPE or DEVNULL; the values are the same as
in subprocess. Errors raise WslError (an OSError carrying .hresult), DistributionNotFoundError, WslNotAvailableError or
TimeoutExpired (also a TimeoutError).
Limitations
- Windows only allows
WslRegisterDistributionfrom a distribution's packaged launcher app. From a regular process it fails withWslError(E_ACCESSDENIED, 0x80070005). Usewsl --importto register a root file system from a script. Process.pidandterminate()refer to the Windows-side host process. Killing it ends the session, so the Linux command is terminated too.- A
Processis not safe to use from several threads at once, except that one thread may write to stdin while another reads stdout or stderr.
Building from source
Prerequisites
- Windows x64.
- Visual Studio 2022 or later (or the Build Tools) with the Desktop development with C++ workload, which includes the Windows SDK
that provides
wslapi.h. - CPython 3.12–3.15 x64.
Build, install and test
python -m pip install -e . pytest
python -m pytest # unit tests only
$env:PYWSL2_TEST_DISTRIBUTION = "Ubuntu" # also run integration tests inside this distribution
python -m pytest
To build a wheel instead: python -m pip install build; python -m build --wheel.
Linting
python -m pip install -r requirements-dev.txt
python -m ruff check .
python -m ruff format --check .
git ls-files '*.cpp' '*.h' | ForEach-Object { clang-format --dry-run --Werror $_ }
Metadata
Release files for pywsl2 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pywsl2-0.1.0.tar.gz | 31.2 kB | Details |
Built distributions (wheels)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pywsl2-0.1.0-cp314-cp314-win_amd64.whl | CPython 3.14 | CPython 3.14 | Windows x86-64 | Details |
| pywsl2-0.1.0-cp313-cp313-win_amd64.whl | CPython 3.13 | CPython 3.13 | Windows x86-64 | Details |
| pywsl2-0.1.0-cp312-cp312-win_amd64.whl | CPython 3.12 | CPython 3.12 | Windows x86-64 | Details |
Total release size: 172.7 kB
Release files / pywsl2-0.1.0.tar.gz
| Download URL | pywsl2-0.1.0.tar.gz |
|---|---|
| Size | 31.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c533e45922a668fe4aa4bbaefe73c649005204bf898015b15016c3f80f1dd93a
|
|
BLAKE2b-256 checksum How to use checksums |
465093661cd08507ef0345e4c3ea499bf01ffceb8a61d19110008f855aca093e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.3
|
Release files / pywsl2-0.1.0-cp314-cp314-win_amd64.whl
| Download URL | pywsl2-0.1.0-cp314-cp314-win_amd64.whl |
|---|---|
| Size | 48.0 kB |
| Tags | CPython 3.14 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
58eec48fe2c74b3cbb32fa657b1906a23a9258642356d0045ffa5bb8112afeb2
|
|
BLAKE2b-256 checksum How to use checksums |
17d6b57c9a393af01c0d0519999aaa3a6898ea224d9b29020446e0e9c53a44f2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.3
|
Release files / pywsl2-0.1.0-cp313-cp313-win_amd64.whl
| Download URL | pywsl2-0.1.0-cp313-cp313-win_amd64.whl |
|---|---|
| Size | 46.7 kB |
| Tags | CPython 3.13 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
c70d1688c4f9bdfbce03c3a301f2457e9ccaedf08fe6f12e6c66c1f049c4b439
|
|
BLAKE2b-256 checksum How to use checksums |
75ed16b302c68836e793b32016f67d9cfa97b5441e6c8589d0da6140a96fdb9e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.3
|
Release files / pywsl2-0.1.0-cp312-cp312-win_amd64.whl
| Download URL | pywsl2-0.1.0-cp312-cp312-win_amd64.whl |
|---|---|
| Size | 46.7 kB |
| Tags | CPython 3.12 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
3b36510d61b26dcdbacbebad24ba4a6ebfa91716bd542bc1c77373197421f4a3
|
|
BLAKE2b-256 checksum How to use checksums |
6556765817c5766b6b10eb8111bff6cd6a25b236780fac627bcc213b6093e6cd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.3
|