Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Deprecated

## [4.34.0] - 2026-03-04

### Fixed

- Fixed f2py and f2py3 detection in Spack environments where the `f2py`
executable lives in the `py-numpy` package's `bin/` directory rather than
alongside the Python interpreter. `FindF2PY.cmake` and `FindF2PY3.cmake` now
ask Python directly (`import numpy.f2py`) to locate the correct `f2py`
binary. A two-pass search is used: the preferred locations (Python `bin/` and
numpy-derived `bin/`) are checked first with `NO_DEFAULT_PATH`, then a normal
`PATH` search is used as a fallback to preserve behaviour on non-Spack
(e.g. module-based) systems.
- Fixed `find_package(Python3)` picking up a different Python 3 interpreter
(e.g. a newer Homebrew Python) instead of the one already found by
`find_package(Python)` in Spack environments. `esma_python.cmake` now pins
`Python3_EXECUTABLE` to `Python_EXECUTABLE` when `find_package(Python)` has
already resolved a Python 3 interpreter, ensuring both find the same Python.

### Changed

- Added `Python_FIND_VIRTUALENV FIRST` and `Python3_FIND_VIRTUALENV FIRST` to
`esma_python.cmake` so that an active Spack `python-venv` on `PATH` is
preferred over system or framework Pythons.

## [4.33.1] - 2026-03-03

### Fixed
Expand Down
14 changes: 14 additions & 0 deletions python/esma_python.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ endif ()
set(Python_FIND_STRATEGY LOCATION)
set(Python_FIND_UNVERSIONED_NAMES FIRST)
set(Python_FIND_FRAMEWORK LAST)
# FIRST: respect an active virtualenv (e.g. Spack python-venv) on PATH before
# falling back to system/Homebrew Python.
set(Python_FIND_VIRTUALENV FIRST)
find_package(Python COMPONENTS Interpreter)
list (APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/f2py")
include (esma_find_python_module)
Expand All @@ -28,6 +31,17 @@ endif ()
set(Python3_FIND_STRATEGY LOCATION)
set(Python3_FIND_UNVERSIONED_NAMES FIRST)
set(Python3_FIND_FRAMEWORK LAST)
# FIRST: respect an active virtualenv (e.g. Spack python-venv) on PATH before
# falling back to system/Homebrew Python.
set(Python3_FIND_VIRTUALENV FIRST)
# If find_package(Python) already found a Python 3 interpreter (e.g. via
# Python_ROOT_DIR set by a Spack environment), pin Python3 to the exact same
# executable so it doesn't wander off and find a different Python 3 (e.g. a
# newer Homebrew Python). Python3_EXECUTABLE bypasses all discovery logic.
if (Python_EXECUTABLE AND Python_VERSION_MAJOR EQUAL 3)
message(DEBUG "[esma_python]: Pinning Python3_EXECUTABLE to ${Python_EXECUTABLE} (already found by find_package(Python))")
set(Python3_EXECUTABLE "${Python_EXECUTABLE}")
endif ()
find_package(Python3 COMPONENTS Interpreter)
list (APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/f2py3")
include (esma_find_python3_module)
Expand Down
34 changes: 34 additions & 0 deletions python/f2py/FindF2PY.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,49 @@ message(DEBUG "[F2PY]: Searching for f2py executable associated with Python_EXEC
get_filename_component(Python_EXECUTABLE_DIR ${Python_EXECUTABLE} DIRECTORY)
message(DEBUG "[F2PY]: Python executable directory: ${Python_EXECUTABLE_DIR}")

# In Spack environments, f2py lives in the py-numpy package's bin dir, which is
# separate from the Python interpreter's bin dir. Ask Python/numpy directly for
# the path to f2py so we always get the one matching the active numpy.
execute_process(
COMMAND "${Python_EXECUTABLE}" -c "import numpy.f2py; import os; print(os.path.dirname(numpy.f2py.__file__))"
OUTPUT_VARIABLE _numpy_f2py_dir
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if (_numpy_f2py_dir)
# numpy.f2py package dir is e.g. .../lib/pythonX.Y/site-packages/numpy/f2py
# Walking up 5 levels reaches the package prefix; bin/ lives alongside lib/.
# Layout: <prefix>/bin/f2py
# lib/pythonX.Y/site-packages/numpy/f2py <- _numpy_f2py_dir
get_filename_component(_numpy_bin_dir "${_numpy_f2py_dir}/../../../../.." ABSOLUTE)
set(_numpy_bin_dir "${_numpy_bin_dir}/bin")
message(DEBUG "[F2PY]: numpy f2py package dir: ${_numpy_f2py_dir}")
message(DEBUG "[F2PY]: numpy-derived bin hint: ${_numpy_bin_dir}")
endif ()

find_program(F2PY_EXECUTABLE
NAMES "f2py${Python_VERSION_MAJOR}"
"f2py${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}"
"f2py-${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}"
"f2py"
PATHS ${Python_EXECUTABLE_DIR}
${_numpy_bin_dir}
HINTS ${Python_EXECUTABLE_DIR}
${_numpy_bin_dir}
NO_DEFAULT_PATH
)

# If not found in the preferred locations, fall back to a normal PATH search.
# This handles non-Spack environments where f2py may be elsewhere on PATH.
if (NOT F2PY_EXECUTABLE)
find_program(F2PY_EXECUTABLE
NAMES "f2py${Python_VERSION_MAJOR}"
"f2py${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}"
"f2py-${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}"
"f2py"
)
endif ()

message(DEBUG "[F2PY]: Found f2py executable: ${F2PY_EXECUTABLE}")

# Now as a sanity check, we need to make sure that the f2py executable is
Expand Down
34 changes: 34 additions & 0 deletions python/f2py3/FindF2PY3.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,49 @@ message(DEBUG "[F2PY3]: Searching for f2py3 executable associated with Python3_E
get_filename_component(Python3_EXECUTABLE_DIR ${Python3_EXECUTABLE} DIRECTORY)
message(DEBUG "[F2PY3]: Python3 executable directory: ${Python3_EXECUTABLE_DIR}")

# In Spack environments, f2py lives in the py-numpy package's bin dir, which is
# separate from the Python interpreter's bin dir. Ask Python/numpy directly for
# the path to f2py so we always get the one matching the active numpy.
execute_process(
COMMAND "${Python3_EXECUTABLE}" -c "import numpy.f2py; import os; print(os.path.dirname(numpy.f2py.__file__))"
OUTPUT_VARIABLE _numpy_f2py_dir
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if (_numpy_f2py_dir)
# numpy.f2py package dir is e.g. .../lib/pythonX.Y/site-packages/numpy/f2py
# Walking up 5 levels reaches the package prefix; bin/ lives alongside lib/.
# Layout: <prefix>/bin/f2py
# lib/pythonX.Y/site-packages/numpy/f2py <- _numpy_f2py_dir
get_filename_component(_numpy_bin_dir "${_numpy_f2py_dir}/../../../../.." ABSOLUTE)
set(_numpy_bin_dir "${_numpy_bin_dir}/bin")
message(DEBUG "[F2PY3]: numpy f2py package dir: ${_numpy_f2py_dir}")
message(DEBUG "[F2PY3]: numpy-derived bin hint: ${_numpy_bin_dir}")
endif ()

find_program(F2PY3_EXECUTABLE
NAMES "f2py${Python3_VERSION_MAJOR}"
"f2py${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR}"
"f2py-${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR}"
"f2py"
PATHS ${Python3_EXECUTABLE_DIR}
${_numpy_bin_dir}
HINTS ${Python3_EXECUTABLE_DIR}
${_numpy_bin_dir}
NO_DEFAULT_PATH
)

# If not found in the preferred locations, fall back to a normal PATH search.
# This handles non-Spack environments where f2py may be elsewhere on PATH.
if (NOT F2PY3_EXECUTABLE)
find_program(F2PY3_EXECUTABLE
NAMES "f2py${Python3_VERSION_MAJOR}"
"f2py${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR}"
"f2py-${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR}"
"f2py"
)
endif ()

message(DEBUG "[F2PY3]: Found f2py3 executable: ${F2PY3_EXECUTABLE}")

# Now as a sanity check, we need to make sure that the f2py3 executable is
Expand Down