Skip to content

Commit b26fbda

Browse files
ru551nclaude
andauthored
Support conda-forge Pythons (#34)
* Use the shared libpython of conda-forge Pythons conda-forge links the python executable statically, so sysconfig reports LDLIBRARY=libpython3.X.a and Py_ENABLE_SHARED=0, but it ships libpython3.X.so in LIBDIR too. The bridge now uses that library and raises the error for a static Python only when there is none. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * CI: conda-forge Python Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 4c6197f commit b26fbda

6 files changed

Lines changed: 43 additions & 9 deletions

File tree

‎.github/workflows/python_bridge.yml‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,16 +125,16 @@ jobs:
125125

126126
#
127127
# Linux with the Pythons users have besides python.org's: the one of the distribution
128-
# (libpython in the multiarch directory), the one uv installs (python-build-standalone)
129-
# and the one pyenv builds from source
128+
# (libpython in the multiarch directory), the one uv installs (python-build-standalone),
129+
# the one pyenv builds from source and conda-forge's (python executable linked statically)
130130
#
131131

132132
pythons:
133133
runs-on: ubuntu-24.04
134134
strategy:
135135
fail-fast: false
136136
matrix:
137-
python: [apt, uv, pyenv]
137+
python: [apt, uv, pyenv, conda]
138138
name: 'ubuntu-24.04 · Python from ${{ matrix.python }} · nvc'
139139
env:
140140
VUNIT_SIMULATOR: nvc
@@ -167,6 +167,11 @@ jobs:
167167
git clone --depth 1 https://github.com/pyenv/pyenv.git "$RUNNER_TEMP/pyenv"
168168
PYENV_ROOT="$RUNNER_TEMP/pyenv" "$RUNNER_TEMP/pyenv/bin/pyenv" install 3.13
169169
echo "PY=$(ls -d "$RUNNER_TEMP"/pyenv/versions/3.13*/bin/python3)" >> "$GITHUB_ENV" ;;
170+
conda)
171+
curl -fsSL -o miniforge.sh https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-Linux-x86_64.sh
172+
bash miniforge.sh -b -p "$RUNNER_TEMP/miniforge"
173+
"$RUNNER_TEMP/miniforge/bin/conda" create -q -y -p "$RUNNER_TEMP/conda" python=3.13
174+
echo "PY=$RUNNER_TEMP/conda/bin/python" >> "$GITHUB_ENV" ;;
170175
esac
171176
172177
# As on python.org's Python: outside the checkout, with a space in the path

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ check(call_integer_vector("gain", arg(integer_vector'(1, 2))) = integer_vector'(
8181
- VUnit 5.0.0.dev12 or later (`vunit_hdl` on PyPI), which pip installs with the package.
8282
- VHDL-2008 or later.
8383
- CPython 3.10 or later, standard (GIL) build, with a shared `libpython` (`--enable-shared`), which
84-
is what distribution Pythons, `actions/setup-python`, `uv` and `pyenv` provide by default.
84+
is what distribution Pythons, `actions/setup-python`, `uv`, `pyenv` and conda-forge provide by default.
8585
- Linux and macOS: a C compiler (`cc`, `gcc` or `clang`, or `CC`) and the Python development headers
8686
(for example the `python3-dev` package). The bridge library is compiled on first use and cached.
8787
- Windows: a 64-bit CPython from python.org (or compatible). The package ships prebuilt DLLs

‎docs/requirements.rst‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ Requirements
1717
:ref:`python_bridge:native`. Python must provide a shared ``libpython``
1818
(``--enable-shared``), which is the case for distribution Pythons,
1919
``actions/setup-python``, ``uv`` and ``pyenv`` builds with default settings.
20+
conda-forge Pythons link the ``python`` executable statically but also
21+
ship the shared library, which the bridge uses.
2022
* Windows: a 64-bit CPython from python.org (or compatible, such as
2123
``actions/setup-python`` and ``uv``). No compiler is needed for NVC and GHDL
2224
when the package includes the prebuilt DLLs. Otherwise, and always for

‎release_notes/news.d/34.feature.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
conda-forge Pythons are supported: their `python` executable links Python statically, but the bridge now uses the shared `libpython` they also ship.

‎src/vunit_python_bridge/native_library.py‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,16 +229,22 @@ def _python_library() -> Path:
229229
230230
Regular --enable-shared builds name it in LDLIBRARY (libpython3.X.so, .dylib); macOS
231231
framework builds name the framework binary instead (Python.framework/Versions/3.X/Python).
232+
conda-forge links its python executable statically but ships the shared library in LIBDIR too.
232233
"""
233234
ldlibrary = sysconfig.get_config_var("LDLIBRARY") or ""
234235
framework = sysconfig.get_config_var("PYTHONFRAMEWORK") or ""
235236
version = f"{sys.version_info[0]}.{sys.version_info[1]}{getattr(sys, 'abiflags', '')}"
236237
shared = bool(sysconfig.get_config_var("Py_ENABLE_SHARED")) or bool(framework)
237238
if not shared or not (framework or ldlibrary.endswith((".so", ".dylib")) or ".so." in ldlibrary):
239+
suffix = ".dylib" if sys.platform == "darwin" else ".so"
240+
shared_library = Path(sysconfig.get_config_var("LIBDIR") or "") / f"libpython{version}{suffix}"
241+
if shared_library.is_file():
242+
return shared_library.resolve()
238243
raise PythonBridgeError(
239244
f"VHDL Python support requires a CPython built with a shared Python library (--enable-shared). "
240245
f"{sys.executable} links Python statically "
241-
f"(LDLIBRARY={ldlibrary!r}, Py_ENABLE_SHARED={sysconfig.get_config_var('Py_ENABLE_SHARED')!r})."
246+
f"(LDLIBRARY={ldlibrary!r}, Py_ENABLE_SHARED={sysconfig.get_config_var('Py_ENABLE_SHARED')!r}) "
247+
f"and there is no {shared_library}."
242248
)
243249
prefix = Path(sys.base_prefix)
244250
candidates = [Path(sysconfig.get_config_var("LIBDIR") or "") / ldlibrary]

‎tests/test_python_bridge.py‎

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -725,7 +725,10 @@ def fake_get_paths():
725725
with self.assertRaisesRegex(RuntimeError, "Python.h"):
726726
self._setup()
727727

728-
def test_static_only_python_raises(self):
728+
def _static_python(self, libdir):
729+
"""
730+
sysconfig of a statically linked Python with libdir as LIBDIR
731+
"""
729732
real_get_config_var = native_library.sysconfig.get_config_var
730733

731734
def fake_get_config_var(name):
@@ -734,11 +737,28 @@ def fake_get_config_var(name):
734737
return 0
735738
if name == "PYTHONFRAMEWORK":
736739
return ""
740+
if name == "LIBDIR":
741+
return str(libdir)
737742
return real_get_config_var(name)
738743

739-
with mock.patch("vunit_python_bridge.native_library.sysconfig.get_config_var", side_effect=fake_get_config_var):
740-
with self.assertRaisesRegex(RuntimeError, "shared Python library"):
741-
self._setup()
744+
return mock.patch.object(native_library.sysconfig, "get_config_var", side_effect=fake_get_config_var)
745+
746+
def test_static_only_python_raises(self):
747+
libdir = self.tempdir / "static lib"
748+
libdir.mkdir()
749+
with self._static_python(libdir):
750+
with self.assertRaisesRegex(RuntimeError, f"shared Python library.*no {re.escape(str(libdir))}"):
751+
native_library._python_library()
752+
753+
def test_static_python_with_a_shared_library_uses_it(self):
754+
# conda-forge: the python executable is static, LIBDIR has libpython3.X.so too
755+
libdir = self.tempdir / "conda lib"
756+
libdir.mkdir()
757+
version = f"{sys.version_info[0]}.{sys.version_info[1]}{getattr(sys, 'abiflags', '')}"
758+
library = libdir / f"libpython{version}{'.dylib' if sys.platform == 'darwin' else '.so'}"
759+
library.write_bytes(b"")
760+
with self._static_python(libdir):
761+
self.assertEqual(native_library._python_library(), library.resolve())
742762

743763
def test_free_threaded_python_raises(self):
744764
real_get_config_var = native_library.sysconfig.get_config_var

0 commit comments

Comments
 (0)