Building and publishing
Prerequisites
- Python 3.11+ and uv
- Qt 6.10+ matching the PySide6/shiboken6 version resolved by
uv sync(currently 6.11.x), including the Qt 5 Compatibility module (Qt6Core5Compat) -- not installed by default, add it via the Qt Maintenance Tool/online installer for your kit. - A C++17 compiler:
- Windows: MSVC (Visual Studio 2022). Use the same MSVC toolchain PySide6's wheels are built with, to avoid ABI mismatches against the Qt/shiboken libraries bundled in the PySide6 wheel.
- macOS: Xcode command line tools.
- Linux: GCC or Clang.
- CMake 3.25+ and Ninja -- available as dev dependencies via
uv, or from<Qt>/Tools/CMake_64/<Qt>/Tools/Ninjaif you installed them through the Qt installer.
Local development build
The build is driven by uv sync (scikit-build-core + CMake +
shiboken_generator_create_binding()). CMake needs CMAKE_PREFIX_PATH
pointing at your Qt 6 installation:
# Windows (PowerShell)
$env:CMAKE_PREFIX_PATH = "C:\Qt\6.11.1\msvc2022_64"
uv sync
# Linux / macOS
export CMAKE_PREFIX_PATH=/path/to/Qt/6.11.1/gcc_64 # or macos/clang_64, etc.
uv sync
Tip
Windows: to avoid setting CMAKE_PREFIX_PATH every session, persist it
to the user environment instead:
$existing = [System.Environment]::GetEnvironmentVariable("CMAKE_PREFIX_PATH", "User")
$new = if ($existing) { "$existing;C:\Qt\6.11.1\msvc2022_64" } else { "C:\Qt\6.11.1\msvc2022_64" }
[System.Environment]::SetEnvironmentVariable("CMAKE_PREFIX_PATH", $new, "User")
Restart your terminal (or VS Code) afterward -- the new value only applies to terminals opened after it's set.
Forcing a rebuild
uv sync doesn't always notice changes to CMakeLists.txt or the typesystem
files -- it may print "Checked N packages" without rebuilding. Force a
rebuild + reinstall with:
uv sync --reinstall-package pyside6-scintilla
Faster C++/binding-only iteration
For changes to CMakeLists.txt, bindings.xml, or the binding glue, uv
sync --reinstall-package does a clean, isolated rebuild every time. The
configure/build/install Make targets are faster for repeated
iteration: they reuse an incremental CMake build tree (build/venv/) and,
via the venv preset's CMAKE_INSTALL_PREFIX, drop the rebuilt extension
and shared libs directly into src/pyside6_scintilla/, where import
pyside6_scintilla picks them up immediately.
make configure # once, to set up build/venv/
make install # rebuild + drop the extension into src/pyside6_scintilla/
Windows: the venv preset uses the Ninja generator, which needs
cl.exe on PATH -- run these from an x64 Native Tools Command Prompt
for VS 2022 (or call vcvarsall.bat x64 first). uv sync doesn't need
this, since scikit-build-core uses the Visual Studio generator, which
locates MSVC itself.
Verifying the build
uv run python -c "from pyside6_scintilla import Scintilla, ScintillaEditBase; print(Scintilla.Message.SetText)"
uv run pytest
uv run ruff check .
uv run pyright
or, via the Makefile:
make setup
make test
make lint
Continuous integration
.github/workflows/ci.yml runs on every push and on pull requests targeting
master, across ubuntu-latest, windows-latest, and ubuntu-24.04-arm
(Linux x86_64/aarch64 + Windows x86_64). Each job:
- Installs Qt 6.11.1 plus the
Qt6Core5Compatmodule viajurplel/install-qt-action-- matching the PySide6 6.11.1 pinned inuv.lock, since CI and local builds must use the same Qt minor version to avoid ABI mismatches (see Prerequisites above). - Points
CMAKE_PREFIX_PATHat that Qt install, and on Linux also setsLD_LIBRARY_PATHsoscintilla_qt.socan findlibQt6Core5Compat.so.6at runtime (see bindings.md issue 3). - Runs
uv sync,uv run pytest(withQT_QPA_PLATFORM=offscreenfor a headlesspytest-qt), anduv run ruff check ./ruff format --check ./uv run pyright-- the same commands as Verifying the build above.
CI doesn't run the example app, and only builds the dev install (not wheels) -- see Building wheels below for the wheel-building workflow.
Building wheels
uv build
produces a wheel + sdist in dist/, for the current platform only.
Multi-platform wheels are built in CI by
.github/workflows/wheels.yml via cibuildwheel,
covering cp31{1,2,3,4} for Linux x86_64 (manylinux_2_34), Windows x86_64,
and macOS arm64/x86_64 (the latter cross-built via Rosetta 2 on the
macos-latest runner). It's workflow_dispatch-only for manual/ad-hoc builds,
and also exposed as a reusable workflow_call job consumed by
publish.yml (see Publishing to PyPI).
Platform-specific bundling caveats
Qt6Core5Compat is required by scintilla_qt but isn't bundled in the
PySide6 wheel on any platform. bindings/CMakeLists.txt installs it
alongside the extension for all three platforms (a flat .dll/.so on
Windows/Linux, a framework bundle on macOS), and each platform's
repair-wheel-command in pyproject.toml
(delvewheel/auditwheel/delocate) excludes the Qt/PySide6/shiboken6
libraries provided at runtime by the installed PySide6 package. See
bindings.md issue 3 for the details and history.
Publishing to PyPI
- Bump
__version__insrc/pyside6_scintilla/__init__.py-- this is the single source of truth for the package version ([tool.scikit-build.metadata.version]inpyproject.tomlreads it via regex). Follow Scintilla's version:X.Y.Z.N, whereX.Y.Zis the vendored Scintilla release andNincrements for binding-only changes against that release. See bindings.md for the Scintilla update process. - Push the version bump to
master. - Create a GitHub Release with a tag matching the version (e.g.
v5.6.3.0). This triggers.github/workflows/publish.yml, which builds the sdist and all wheels (viawheels.yml), publishes them to TestPyPI, and then -- for real releases only -- to PyPI.
No PyPI API tokens are involved: both the TestPyPI and PyPI publish steps use
PyPI trusted publishing (GitHub
OIDC), configured per-project on test.pypi.org/pypi.org against this repo's
publish.yml workflow and the testpypi/pypi GitHub environments.
The pyside6-scintilla PyPI name was claimed early with a placeholder 0.0.0
release (pure-Python, hatchling backend, published manually via uv publish)
to prevent squatting -- the first real release is the first one published via
publish.yml.