Skip to main content

Python 3.15.0rc1 on conda-forge, a leaner libpython, and a more unixy Windows layout

· 6 min read
Isuru Fernando
Member of conda-forge/core

Following the release of Python 3.15.0rc1, conda-forge has started building Python 3.15 packages. You can create an environment with the Python 3.15 release candidate by running the command:

conda create -n py315 python=3.15 -c conda-forge -c conda-forge/label/python_rc

As with previous feature releases, we start building against the release candidate because the ABI is guaranteed to be stable from here to the final 3.15.0. This is also a release where the python package on Windows looks noticeably different on disk, and where the shared library that every python package used to carry has moved into its own package.

Note that at this early stage only the python interpreter itself, noarch packages, and extensions built against the stable ABI (abi3) are available for 3.15. The broader rebuild of compiled packages hasn't started yet — that begins once a python315 entry lands in conda-forge-pinning, the same way it did for 3.14 and 3.13. That initial migration will only cover the regular (non-freethreading) builds — the 3.14t migration keeps running in the meantime, and a 3.15t migration will start at a later date, the same way 3.14t followed 3.13t (conda-forge/conda-forge-pinning-feedstock#8891).

A more unixy layout on Windows

For as long as conda-forge has shipped Python on Windows, the layout inside an environment has looked like a regular Windows Python install: a flat Lib\ for the standard library and site-packages, a top-level DLLs\ for compiled extension modules, include\ for headers, and libs\ for import libraries. On Unix, the same content is grouped under a single lib/pythonX.Y/ tree. That divergence meant every other package uses <PREFIX>\Library\ instead of <PREFIX>\ on windows.

Starting with 3.15, conda-forge's Windows builds move to a layout that mirrors the Unix one:

contentold path (≤3.14)new path (3.15+)
standard libraryLib\lib\python\
site-packagesLib\site-packages\lib\python\site-packages\
compiled extension modules (.pyd)DLLs\lib\python\lib-dynload\
Tools\ scriptsTools\lib\python\Tools\
headers, pyconfig.hinclude\include\python\

Scripts\, libs\, and the top-level python.exe/pythonw.exe/python3.dll are unchanged. This is purely about conda-forge's own Python build layout — it doesn't touch the general Library\ convention that other conda packages use for their headers and DLLs on Windows.

This will help to remove the distinction between <PREFIX>/Library and <PREFIX> in the future, but we will keep the distinction for now.

See conda-forge/python-feedstock#873 for the change and CFEP-27 for the CFEP that explains the rationale.

The shared library moves into its own libpython package

The python package has always carried the shared library — libpythonX.Y.so/.dylib even though most environments never touch it directly. It's only needed for apps that embed python. This is because on Unix, the python executable links against the static libpython library for performance reasons. Every other package that just runs Python or uses compiled extensions was paying for bytes it didn't use.

conda-forge already split the static archive out into libpython-static some time ago. For 3.15, the shared library gets the same treatment: a new libpython output now carries it, and python is simply run_constrained by an exact pin of libpython. Installing python now doesn't give you the shared library and applications that embed python need to depend on libpython directly, and environments that don't need it can end up smaller. This work started on the 3.15 branch and will be backported to previous minor versions, but installing python on those versions will bring in libpython automatically unlike 3.15.

Alongside this, macOS now also gets an unversioned libpython3.dylib for stable-ABI (abi3) consumers, matching the libpython3.so that has long existed on Linux for the same purpose — so applications that embed Python via the stable ABI on macOS no longer need to pin an exact Python minor version to link against. This is distinct from regular Python extension modules built against abi3, which don't link against libpython at all.

Note that libpython is empty on windows, and python*.dll are in the python package since the executables depend on the shared library unlike Unix. Also note that the libpython name was previously used by a Windows-only package from libpython-feedstock that provided a mingw-w64 import library for pythonXY.dll, for use with the m2 (mingw) toolchain. That old meaning has been repurposed to make room for the new libpython output described above — anyone who depended on it for the m2 toolchain should switch to distutils-activate-mingw instead (conda-forge/libpython-feedstock#20).

See conda-forge/python-feedstock#843 for the motivation and #894 and #885 for the implementation across platforms.

conda-forge's Python builds have long carried a python3.1 symlink (and a matching site-packages path) alongside the real interpreter, as a workaround for an old bug in conda (conda/conda#10969) where minor versions with two digits, like 3.10 or 3.15, could be misparsed as 3.1. That bug has long since been fixed, and 3.15 drops the python3.1 symlink and the associated workaround (conda-forge/python-feedstock#912).

Thanks

Thanks to Michał Górny (@mgorny) for the 3.15.0rc1 bump and the original libpython split, and to Axel Obermeier (@h-vetinari) for backporting the libpython PRs to previous versions.

Some of this work was partially funded by Sovereign Tech Agency

Any issues with the python package itself can be reported at python-feedstock.