Repository navigation
setup.py: pass Python_EXECUTABLE to CMake from the build interpreter - #213
Conversation
|
I think we don't suppose the build works with wrong python env/wrong pip. |
|
Agreed that a wrong environment is not supported. The case here is narrower: the right pip (the environment's own, called by full path, or via a tool such as pipx/uv that does not activate the environment) building into the right environment, while a different |
|
I'm not a fan of this approach because having a slightly different version of |
There was a problem hiding this comment.
Comment composed by Claude Opus 5 following a conversation with me locally:
Following up on my earlier comment — I do think there is an elegant fix at the root, and it belongs in CMakeLists.txt rather than setup.py.
First, a data point on the root cause: the probe at pyscf/lib/CMakeLists.txt:99 is CWD-sensitive, not only PATH-sensitive. The forge checkout has no pyscf/__init__.py, so it is a namespace-package portion, and python3 -c puts the working directory at sys.path[0]. From the checkout root, an interpreter without pyscf therefore "finds" the checkout itself:
$ cd pyscf-forge
$ /usr/bin/python3 -c "import pyscf; print(pyscf.__path__[0], end='')"
/path/to/pyscf-forge/pyscf # exit status 0, no error
$ cd /tmp && /usr/bin/python3 -c "import pyscf; print(pyscf.__path__[0], end='')"
ModuleNotFoundError: No module named 'pyscf'Second, on why I would rather not duplicate the lookup in setup.py: the two spellings already disagree, on exactly the input that triggers this bug.
CMakeLists (pyscf.__path__[0]) -> /path/to/pyscf-forge/pyscf wrong, silent
this PR (os.path.dirname(__file__)) -> TypeError: ... not NoneType namespace pkg: __file__ is None
In the PR as written that TypeError is swallowed — subprocess.run has no check and stderr is not surfaced — so pyscf_dir ends up empty, no -D is appended, and the build falls back to the original broken path. That is precisely the failure mode I had in mind when I said two versions of the same operation adds surface area.
Since the root cause is the hardcoded python3, I would suggest passing the interpreter rather than the answer:
find_package(Python COMPONENTS Interpreter REQUIRED)
if(NOT PYSCF_SOURCE_DIR)
execute_process(
COMMAND ${Python_EXECUTABLE} -c "import pyscf; print(pyscf.__path__[0], end='')"
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
OUTPUT_VARIABLE PYSCF_SOURCE_DIR
RESULT_VARIABLE _pyscf_rc)
if(NOT _pyscf_rc EQUAL 0 OR NOT EXISTS "${PYSCF_SOURCE_DIR}/lib/config.h.in")
message(FATAL_ERROR
"Could not locate an installed pyscf using ${Python_EXECUTABLE}. "
"Pass -DPYSCF_SOURCE_DIR=<.../site-packages/pyscf>.")
endif()
endif()plus a single line in setup.py, next to the other configure args:
cmd.append(f'-DPython_EXECUTABLE={sys.executable}')This keeps the "find pyscf" command in exactly one place and setup.py never learns how to locate pyscf, which addresses my original concern. Python_EXECUTABLE is a standard CMake variable (scikit-build and pybind11 key off the same one), so it helps other consumers as well. WORKING_DIRECTORY removes the CWD shadowing shown above, and the config.h.in existence check converts your second failure mode — a different pyscf first on PATH, silently building against the wrong headers — into an actionable error rather than a mislinked binary. That case matters more than the error message, because configure_file at lines 105-107 writes config.h into ${PYSCF_SOURCE_DIR}/lib/, i.e. into the installed pyscf.
One unrelated nit in the same block: option() at line 96 is the wrong command for a path. It declares a BOOL cache entry, defaulting to OFF. It should be:
set(PYSCF_SOURCE_DIR "" CACHE PATH "Pyscf source directory e.g. /usr/local/lib/python3.7/site-packages/pyscf")Would you be willing to rework the PR along these lines?
MatthewRHermes
left a comment
There was a problem hiding this comment.
Please rework this so that it doesn't rely on copy-paste-modify of a line from CMakeLists.txt
…ld directory, or fail Reworked along the review of 25 September: the pyscf lookup stays in one place (pyscf/lib/CMakeLists.txt) and runs with the interpreter the build runs under (find_package(Python); setup.py passes -DPython_EXECUTABLE=sys.executable), from the build directory instead of the checkout, and fails with a clear message when pyscf is not found or has no lib/config.h.in. PYSCF_SOURCE_DIR is a CACHE PATH instead of an option(). cmake_minimum_required 3.5 -> 3.12 for find_package(Python). Configure step tested on Linux (CMake 4.2): - /usr/bin/python3 (no pyscf) first on PATH, run from the checkout, no -DPython_EXECUTABLE: before, "Include pyscf source dir: <checkout>/pyscf" and config.h.in does not exist; now, "Could not locate an installed pyscf using /usr/bin/python3". - -DPython_EXECUTABLE=<env>/bin/python: "Include pyscf source dir: <env>/site-packages/pyscf", configuring done.
d7a79d6 to
8509117
Compare
|
Reworked along your suggestion, thank you — it is the better fix. Configure step checked on Linux (CMake 4.2), from the checkout with |
…s the first interpreter by location); setup.py keeps its executable bit
|
Thanks, done in 84230da: |
What
pyscf/lib/CMakeLists.txtlocates the installed pyscf, whenPYSCF_SOURCE_DIRis not given, by running the firstpython3onPATH:That interpreter is not necessarily the one the build runs under. Two ways this goes wrong in practice:
pip is called by its absolute path from an environment that is not activated (or the build runs on a system whose
python3has no pyscf). The systempython3, started in the checkout directory, then imports the checkout's ownpyscf/namespace directory, and the configure step fails withif that
python3has another pyscf installed, the extension is silently built against the wrong headers.This PR makes
setup.pyresolve the pyscf location withsys.executable(from a neutral working directory) and pass-DPYSCF_SOURCE_DIR=...to CMake, unlessCMAKE_CONFIGURE_ARGSalready sets it. No change when the environment is activated; no change to the CMake files.Test plan
All four in the same conda environment (Python 3.12, pyscf 2.14.0),
pip install --no-build-isolation --no-deps .,BUILD_OCCRI=OFF, withPATHrestricted so thatpython3is the system interpreter without pyscf (cmake and gcc still reachable):masterInclude pyscf source dir: /root/prs_cmake/pyscf,config.h.in does not existInclude pyscf source dir: <site-packages>/pyscfCMAKE_CONFIGURE_ARGS=-DPYSCF_SOURCE_DIR=...set explicitlyimport pyscf.lno.lnoccsdworks after B–D.🤖 Generated with Claude Code