From 709a434008a907cb14ac4048af289177c3c9e63c Mon Sep 17 00:00:00 2001 From: "Ryan M. Richard" Date: Wed, 1 Jul 2026 21:54:50 -0500 Subject: [PATCH 01/22] first pass --- CMakeLists.txt | 84 ------------------- cmake/ase.cmake | 32 ------- cmake/get_nwx_cmake.cmake | 31 ------- cmake/molssi.cmake | 36 -------- cmake/nwchem.cmake | 30 ------- cmake/python/find_python.cmake | 27 ------ cmake/python/find_python_module.cmake | 59 ------------- cmake/python/python.cmake | 17 ---- pyproject.toml | 60 ++++--------- {src/python => python}/friendzone/__init__.py | 0 {src/python => python}/friendzone/friends.py | 0 .../friendzone/nwx2ase/__init__.py | 0 .../nwx2ase/chemical_system_conversions.py | 0 .../friendzone/nwx2ase/nwchem_via_ase.py | 0 .../friendzone/nwx2molssi/__init__.py | 0 .../friendzone/nwx2molssi/call_qcengine.py | 0 .../nwx2molssi/chemical_system_conversions.py | 0 .../nwx2molssi/nwchem_via_molssi.py | 0 .../nwx2molssi/system_via_molssi.py | 0 .../friendzone/utils/__init__.py | 0 .../friendzone/utils/unwrap_inputs.py | 0 21 files changed, 16 insertions(+), 360 deletions(-) delete mode 100644 CMakeLists.txt delete mode 100644 cmake/ase.cmake delete mode 100644 cmake/get_nwx_cmake.cmake delete mode 100644 cmake/molssi.cmake delete mode 100644 cmake/nwchem.cmake delete mode 100644 cmake/python/find_python.cmake delete mode 100644 cmake/python/find_python_module.cmake delete mode 100644 cmake/python/python.cmake rename {src/python => python}/friendzone/__init__.py (100%) rename {src/python => python}/friendzone/friends.py (100%) rename {src/python => python}/friendzone/nwx2ase/__init__.py (100%) rename {src/python => python}/friendzone/nwx2ase/chemical_system_conversions.py (100%) rename {src/python => python}/friendzone/nwx2ase/nwchem_via_ase.py (100%) rename {src/python => python}/friendzone/nwx2molssi/__init__.py (100%) rename {src/python => python}/friendzone/nwx2molssi/call_qcengine.py (100%) rename {src/python => python}/friendzone/nwx2molssi/chemical_system_conversions.py (100%) rename {src/python => python}/friendzone/nwx2molssi/nwchem_via_molssi.py (100%) rename {src/python => python}/friendzone/nwx2molssi/system_via_molssi.py (100%) rename {src/python => python}/friendzone/utils/__init__.py (100%) rename {src/python => python}/friendzone/utils/unwrap_inputs.py (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index c05c26a..0000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright 2022 NWChemEx-Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -cmake_minimum_required(VERSION 3.14) - -#Downloads common CMake modules used throughout NWChemEx -include(cmake/get_nwx_cmake.cmake) - -#Sets the version to whatever git thinks it is -include(get_version_from_git) -get_version_from_git(friendzone_version "${CMAKE_CURRENT_LIST_DIR}") -project(friendzone VERSION "${friendzone_version}" LANGUAGES CXX) -set(CMAKE_CXX_STANDARD 20) - -include(get_cmaize) -include(nwx_cxx_api_docs) - -### Files and Paths ### -set(python_src_directory "${CMAKE_CURRENT_LIST_DIR}/src/python") - -# # Doxygen docs -nwx_cxx_api_docs("${CMAKE_CURRENT_SOURCE_DIR}/src" "${CMAKE_CURRENT_SOURCE_DIR}/include") - -### Options ### -cmaize_option_list( - BUILD_TESTING OFF "Should we build the tests?" - BUILD_PYBIND11_PYBINDINGS ON "Use Pybind11 to build Python bindings?" - ENABLE_NWCHEM ON "Should we build support for friend: NWChem ?" - ENABLE_MOLSSI ON "Build support for the MolSSI interface?" - ENABLE_ASE ON "Build support for the Atomic Simulation Environment?" -) - -## Build FriendZone's dependencies ## -cmaize_find_or_build_dependency( - simde - URL github.com/NWChemEx/SimDE - BUILD_TARGET simde - FIND_TARGET nwx::simde - CMAKE_ARGS BUILD_TESTING=OFF - BUILD_PYBIND11_PYBINDINGS=${BUILD_PYBIND11_PYBINDINGS} -) - -## Get the CMake utilities for this project ## -set( - CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${PROJECT_SOURCE_DIR}/cmake" - CACHE STRING "" FORCE -) - -## Find friends ## -include(ase) -include(molssi) -include(nwchem) - -#TOOD: Replace cmaize_add_library when it supports Python -add_library(${PROJECT_NAME} INTERFACE) -target_link_libraries(${PROJECT_NAME} INTERFACE simde) - -if("${BUILD_TESTING}") - include(CTest) - include(nwx_pybind11) - set(PYTHON_TEST_DIR "${CMAKE_CURRENT_LIST_DIR}/tests/python") - - nwx_pybind11_tests( - py_${PROJECT_NAME} - "${PYTHON_TEST_DIR}/unit_tests/test_friendzone.py" - SUBMODULES simde chemist pluginplay parallelzone tensorwrapper - ) -endif() - -install( - DIRECTORY "${python_src_directory}/friendzone" - DESTINATION "${NWX_MODULE_DIRECTORY}" -) diff --git a/cmake/ase.cmake b/cmake/ase.cmake deleted file mode 100644 index 99d97ab..0000000 --- a/cmake/ase.cmake +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright 2024 NWChemEx-Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -include_guard() - -if("${BUILD_PYBIND11_PYBINDINGS}") - include(python/python) - - #[[[ Determines if ASE is installed. - # - # At present FriendZone can not install - #]] - function(find_ase) - assert_python_module("ase") - message(STATUS "Found ASE: ${ASE_FOUND}") - endfunction() - - if("${ENABLE_ASE}") - find_ase() - endif() -endif() diff --git a/cmake/get_nwx_cmake.cmake b/cmake/get_nwx_cmake.cmake deleted file mode 100644 index c99674d..0000000 --- a/cmake/get_nwx_cmake.cmake +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright 2024 NWChemEx-Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -include_guard() - -macro(get_nwx_cmake) - include(FetchContent) - FetchContent_Declare( - nwx_cmake - GIT_REPOSITORY https://github.com/NWChemEx/NWXCMake - ) - FetchContent_MakeAvailable(nwx_cmake) - set( - CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${nwx_cmake_SOURCE_DIR}/cmake" - CACHE STRING "" - FORCE - ) -endmacro() - -get_nwx_cmake() diff --git a/cmake/molssi.cmake b/cmake/molssi.cmake deleted file mode 100644 index 3341ee5..0000000 --- a/cmake/molssi.cmake +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright 2023 NWChemEx-Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -include_guard() - -if("${BUILD_PYBIND11_PYBINDINGS}") - include(python/python) - - #[[[ Determines if the MolSSI Python interface are installed. - # - # At present FriendZone can not install - #]] - function(find_molssi) - assert_python_module("qcelemental") - message(STATUS "Found qcelemental: ${QCELEMENTAL_FOUND}") - assert_python_module("qcengine") - message(STATUS "Found qcengine: ${QCENGINE_FOUND}") - assert_python_module("networkx") - message(STATUS "Found networkx: ${NETWORKX_FOUND}") - endfunction() - - if("${ENABLE_MOLSSI}") - find_molssi() - endif() -endif() diff --git a/cmake/nwchem.cmake b/cmake/nwchem.cmake deleted file mode 100644 index 26f1879..0000000 --- a/cmake/nwchem.cmake +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2023 NWChemEx-Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -include_guard() - -if("${BUILD_PYBIND11_PYBINDINGS}") - #[[[ Determines if NWChem and the necessary Python interface are installed. - # - # At present FriendZone can not install - #]] - function(find_nwchem) - find_program(NWCHEM_FOUND nwchem REQUIRED) - message(STATUS "Found nwchem: ${NWCHEM_FOUND}") - endfunction() - - if("${ENABLE_NWCHEM}") - find_nwchem() - endif() -endif() diff --git a/cmake/python/find_python.cmake b/cmake/python/find_python.cmake deleted file mode 100644 index 5a378f7..0000000 --- a/cmake/python/find_python.cmake +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright 2023 NWChemEx-Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -include_guard() - -#[[[ Wraps the process of finding the Python interpreter. -# -# At the moment this is a thin wrapper around find_package with our preffered -# options. -#]] -function(find_python) - find_package(Python COMPONENTS Interpreter QUIET REQUIRED) - message(STATUS "Found Python: ${Python_EXECUTABLE}") -endfunction() - -find_python() diff --git a/cmake/python/find_python_module.cmake b/cmake/python/find_python_module.cmake deleted file mode 100644 index 426584e..0000000 --- a/cmake/python/find_python_module.cmake +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright 2023 NWChemEx-Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -include_guard() - -#[[[ Attempts to find a Python module, returns whether it was found or not. -# -# This function will invoke the pip module through the Python interpreter and -# see if the specified module appears in the list of installed modules. The -# result of this inquiry is then returned -# -# :param was_found: Used to return the result -# :type was_found: *bool -# :param module_name: The name of the Python module to look for. -# :type module_name: str -#]] -function(find_python_module fpm_was_found fpm_module_name) - execute_process( - COMMAND "${Python_EXECUTABLE}" "-m" "pip" "list" - COMMAND grep -w "${fpm_module_name}" - OUTPUT_VARIABLE _fpm_modules - ) - if("${_fpm_modules}" STREQUAL "") - set("${fpm_was_found}" FALSE PARENT_SCOPE) - else() - set("${fpm_was_found}" TRUE PARENT_SCOPE) - endif() -endfunction() - -#[[[ Raises a fatal error if a Python module is not installed. -# -# This function is a thin wrapper around ``find_python_module`` that asserts -# that the return value of ``find_python_module`` is TRUE. If the value -# returned from ``find_python_module`` is not TRUE this function will raise -# a fatal error. -# -# :param module_name: The name of the Python module which must be installed. -# :type module_name: str -#]] -function(assert_python_module apm_module_name) - find_python_module(_apm_was_found "${apm_module_name}") - if(NOT "${_apm_was_found}") - message( - FATAL_ERROR - "Unable to locate Python module: ${apm_module_name}" - ) - endif() -endfunction() diff --git a/cmake/python/python.cmake b/cmake/python/python.cmake deleted file mode 100644 index 9a4b7f8..0000000 --- a/cmake/python/python.cmake +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright 2023 NWChemEx-Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -include_guard() -include(python/find_python) -include(python/find_python_module) diff --git a/pyproject.toml b/pyproject.toml index 7e50ebe..35ba1d4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,35 +12,18 @@ # See the License for the specific language governing permissions and # limitations under the License. -# This file defines the pip-installable Python package. - [build-system] -requires = ["setuptools>=46.1.0", "setuptools_scm[toml]>=5"] +requires = ["setuptools>=64", "setuptools-scm>=8"] build-backend = "setuptools.build_meta" -[tool.setuptools_scm] -# For smarter version schemes and other configuration options, -# check out https://github.com/pypa/setuptools_scm -version_scheme = "no-guess-dev" - -# To create a pip-installable package that uses CMake in the backend, -# scikit-build-core is used as the build backend, recommended by Pybind11. -# -# scikit-build-core: https://scikit-build-core.readthedocs.io/en/latest/ -# Pybind11 scikit-build-core example: https://github.com/pybind/scikit_build_example -# [build-system] -# requires = ["scikit-build-core>=0.11", "pybind11>=3.0"] -# build-backend = "scikit_build_core.build" - - [project] -name = "friendzone" -license = "Apache-2.0" -license-files = ["LICENSE"] +name = "nwchemex-friendzone" description = "Provides SimDE compatible APIs so that NWChemEx can play nicely with its friends." readme = "README.md" +license = "Apache-2.0" +license-files = ["LICENSE"] authors = [ - { name = "zachcran", email = "zachcran@iastate.edu" }, + { name = "zachcran", email = "zachcran@iastate.edu" }, { name = "jwaldrop107", email = "jwaldrop@ameslab.gov" }, ] requires-python = ">=3.10" @@ -54,33 +37,22 @@ classifiers = [ "Programming Language :: Python :: 3.14", "Private :: Do Not Upload", ] -# Dynamic project attributes -# -# Set the project version dynamically according to git tags. -# Git tag version info: https://github.com/pypa/setuptools-scm/blob/fb261332d9b46aa5a258042d85baa5aa7b9f4fa2/README.rst#default-versioning-scheme dynamic = ["version"] -# NOTE: Invisible dependency for now until Python bindings at SimDE are -# packaged properly and available here -# dependencies = ["simde"] - +# NOTE: nwchemex-simde is not yet pip-installable; enable once it is. +# dependencies = ["nwchemex-simde"] -[tool.setuptools] -# Cannot automatically find the friendzone namespace so we set it here -package-dir = { "" = "src/python" } - -# Optional dependencies represent optional features that can be enabled -# during installation -# Example: pip install friendzone[ase] [project.optional-dependencies] -ase = ["ase"] +ase = ["ase"] molssi = ["qcengine", "qcelemental", "networkx"] +test = ["pytest"] +dev = ["nwchemex-friendzone[test]", "tox", "pre-commit"] + +# Python source lives under python/ (analogous to cxx/ in C++ repos). +[tool.setuptools.packages.find] +where = ["python"] -# Dependency groups are optional dependencies that are not intented to appear -# after packaging, usually used to help with testing or development -# Example: pip install --group dev -[dependency-groups] -test = ["pytest"] -dev = [{ include-group = "test" }, "tox", "pre-commit"] +[tool.setuptools_scm] +fallback_version = "0.0.0" [tool.pytest.ini_options] minversion = "8.0" diff --git a/src/python/friendzone/__init__.py b/python/friendzone/__init__.py similarity index 100% rename from src/python/friendzone/__init__.py rename to python/friendzone/__init__.py diff --git a/src/python/friendzone/friends.py b/python/friendzone/friends.py similarity index 100% rename from src/python/friendzone/friends.py rename to python/friendzone/friends.py diff --git a/src/python/friendzone/nwx2ase/__init__.py b/python/friendzone/nwx2ase/__init__.py similarity index 100% rename from src/python/friendzone/nwx2ase/__init__.py rename to python/friendzone/nwx2ase/__init__.py diff --git a/src/python/friendzone/nwx2ase/chemical_system_conversions.py b/python/friendzone/nwx2ase/chemical_system_conversions.py similarity index 100% rename from src/python/friendzone/nwx2ase/chemical_system_conversions.py rename to python/friendzone/nwx2ase/chemical_system_conversions.py diff --git a/src/python/friendzone/nwx2ase/nwchem_via_ase.py b/python/friendzone/nwx2ase/nwchem_via_ase.py similarity index 100% rename from src/python/friendzone/nwx2ase/nwchem_via_ase.py rename to python/friendzone/nwx2ase/nwchem_via_ase.py diff --git a/src/python/friendzone/nwx2molssi/__init__.py b/python/friendzone/nwx2molssi/__init__.py similarity index 100% rename from src/python/friendzone/nwx2molssi/__init__.py rename to python/friendzone/nwx2molssi/__init__.py diff --git a/src/python/friendzone/nwx2molssi/call_qcengine.py b/python/friendzone/nwx2molssi/call_qcengine.py similarity index 100% rename from src/python/friendzone/nwx2molssi/call_qcengine.py rename to python/friendzone/nwx2molssi/call_qcengine.py diff --git a/src/python/friendzone/nwx2molssi/chemical_system_conversions.py b/python/friendzone/nwx2molssi/chemical_system_conversions.py similarity index 100% rename from src/python/friendzone/nwx2molssi/chemical_system_conversions.py rename to python/friendzone/nwx2molssi/chemical_system_conversions.py diff --git a/src/python/friendzone/nwx2molssi/nwchem_via_molssi.py b/python/friendzone/nwx2molssi/nwchem_via_molssi.py similarity index 100% rename from src/python/friendzone/nwx2molssi/nwchem_via_molssi.py rename to python/friendzone/nwx2molssi/nwchem_via_molssi.py diff --git a/src/python/friendzone/nwx2molssi/system_via_molssi.py b/python/friendzone/nwx2molssi/system_via_molssi.py similarity index 100% rename from src/python/friendzone/nwx2molssi/system_via_molssi.py rename to python/friendzone/nwx2molssi/system_via_molssi.py diff --git a/src/python/friendzone/utils/__init__.py b/python/friendzone/utils/__init__.py similarity index 100% rename from src/python/friendzone/utils/__init__.py rename to python/friendzone/utils/__init__.py diff --git a/src/python/friendzone/utils/unwrap_inputs.py b/python/friendzone/utils/unwrap_inputs.py similarity index 100% rename from src/python/friendzone/utils/unwrap_inputs.py rename to python/friendzone/utils/unwrap_inputs.py From 92fc44c4f55c36a0fd8c97b3d3b6bf5370e1e539 Mon Sep 17 00:00:00 2001 From: "Ryan M. Richard" Date: Thu, 2 Jul 2026 08:59:11 -0500 Subject: [PATCH 02/22] adds upstream dependencies --- pyproject.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 35ba1d4..7dd49ec 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,8 +38,7 @@ classifiers = [ "Private :: Do Not Upload", ] dynamic = ["version"] -# NOTE: nwchemex-simde is not yet pip-installable; enable once it is. -# dependencies = ["nwchemex-simde"] +dependencies = ["nwchemex-simde"] [project.optional-dependencies] ase = ["ase"] From b46207b7a77cb894c74bfad85c8fd523364bae59 Mon Sep 17 00:00:00 2001 From: "Ryan M. Richard" Date: Sat, 11 Jul 2026 12:48:18 -0500 Subject: [PATCH 03/22] Migrate to fork-tested CI/CD + TestPyPI publish infrastructure Remove the 'Private :: Do Not Upload' PyPI classifier, which per PyPI's own documented behavior causes any upload (including to TestPyPI) to be permanently rejected outright -- a hard blocker for publishing. Switch pull_request and merge workflows from NWChemEx/.github + master to ryanmrichard/.github + build_overhaul. FriendZone is pure Python (no CMakeLists.txt, confirmed by an earlier de-C++-ify commit in this branch's history), so this uses a new pattern rather than the compiled-repo template: doc_target: "Sphinx" skips the nonexistent Doxygen/CMake doc build (deploy_nwx_docs.yaml/test_nwx_docs.yaml both document this literal special value), and a lean OS-only test_python job (no compiler axis -- nothing to compile) replaces the old CMake-oriented test_library job, modeled on NWXCMake's own working pure-Python CI. The release job similarly reuses build_pypi_dist's existing plain-Python path (cibw_build left unset), exactly matching NWXCMake's already-working merge.yaml. Delete stale root requirements.txt: unreferenced by any CI workflow, inconsistent with pyproject.toml (pydantic isn't a real dependency, confirmed unimported anywhere under python/friendzone), superseded by pyproject.toml's optional-dependencies as the single source of truth. Delete tests/python/unit_tests/test_friendzone.py: dead code (only an if __name__ == "__main__" block, references a src/python path that no longer exists post-move to python/friendzone). Add tests/python/conftest.py with the session-scoped RuntimeView fixture, for consistency with the rest of the ecosystem even though no current FriendZone test constructs RuntimeView directly. --- .github/workflows/merge.yaml | 28 +++++++++-- .github/workflows/pull_request.yaml | 54 ++++++++++++++++++---- pyproject.toml | 1 - requirements.txt | 4 -- tests/python/conftest.py | 35 ++++++++++++++ tests/python/unit_tests/test_friendzone.py | 35 -------------- 6 files changed, 104 insertions(+), 53 deletions(-) delete mode 100644 requirements.txt create mode 100644 tests/python/conftest.py delete mode 100644 tests/python/unit_tests/test_friendzone.py diff --git a/.github/workflows/merge.yaml b/.github/workflows/merge.yaml index 37236af..ce884a6 100644 --- a/.github/workflows/merge.yaml +++ b/.github/workflows/merge.yaml @@ -18,16 +18,34 @@ name: Merge Workflow on: push: branches: - - master + - build_overhaul jobs: tag-commit: - uses: NWChemEx/.github/.github/workflows/tag.yaml@master + uses: ryanmrichard/.github/.github/workflows/tag.yaml@master secrets: inherit deploy_nwx_docs: - uses: NWChemEx/.github/.github/workflows/deploy_nwx_docs.yaml@master + uses: ryanmrichard/.github/.github/workflows/deploy_nwx_docs.yaml@master with: - doc_target: "friendzone_cxx_api" - generate_module_docs: true + doc_target: "Sphinx" secrets: inherit + + # No platform_matrix / cibw_build needed: FriendZone is pure Python, so + # build_pypi_dist's plain path (cibw_build left unset) applies -- a single + # platform-independent sdist+wheel, same shape as NWXCMake's own working + # merge.yaml. + deploy_to_pypi: + needs: tag-commit + runs-on: ubuntu-latest + permissions: + id-token: write + steps: + - name: Build PyPI Distribution + uses: ryanmrichard/.github/.github/actions/build_pypi_dist@master + + - name: Publish to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + packages-dir: dist/ + repository-url: https://test.pypi.org/legacy/ diff --git a/.github/workflows/pull_request.yaml b/.github/workflows/pull_request.yaml index ce1669a..b75025a 100644 --- a/.github/workflows/pull_request.yaml +++ b/.github/workflows/pull_request.yaml @@ -18,18 +18,56 @@ name: Pull Request Workflow on: pull_request: branches: - - master + - build_overhaul jobs: check_formatting: - uses: NWChemEx/.github/.github/workflows/check_formatting.yaml@master + uses: ryanmrichard/.github/.github/workflows/check_formatting.yaml@master test_nwx_docs: - uses: NWChemEx/.github/.github/workflows/test_nwx_docs.yaml@master + uses: ryanmrichard/.github/.github/workflows/test_nwx_docs.yaml@master with: - doc_target: "friendzone_cxx_api" + doc_target: "Sphinx" - test_library: - uses: NWChemEx/.github/.github/workflows/test_nwx_library.yaml@master - with: - compilers: '["gcc-14", "clang-18"]' + # FriendZone is pure Python (no CMakeLists.txt), so there's nothing to + # compile and the compiler axis of the shared dev matrix is meaningless + # here -- just a lean OS-only matrix. setup_nwx_dev_env still runs (via its + # unconditional MPI/Boost install) because nwchemex-simde's prebuilt wheel + # dynamically links against libmpi at import time, and bare GitHub runners + # have no MPI by default. + test_python: + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest] + runs-on: ${{ matrix.os }} + steps: + - name: Checkout Source + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.x" + - name: Setup NWX Dev Environment + uses: ryanmrichard/.github/.github/actions/setup_nwx_dev_env@master + with: + compiler: gcc-14 + - name: Install + shell: bash + run: | + pip install -e ".[dev]" --extra-index-url https://test.pypi.org/simple/ + - name: Test Python + shell: bash + run: | + set +e + pytest -v + pytest_exit=$? + set -e + + if [ "$pytest_exit" -eq 5 ]; then + echo "::error::zero pytest tests were found." + exit 1 + fi + exit "$pytest_exit" diff --git a/pyproject.toml b/pyproject.toml index 7dd49ec..aa9e7a1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,7 +35,6 @@ classifiers = [ "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", - "Private :: Do Not Upload", ] dynamic = ["version"] dependencies = ["nwchemex-simde"] diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 1fd6065..0000000 --- a/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ -ase -networkx -pydantic>=1.10.13 -qcengine diff --git a/tests/python/conftest.py b/tests/python/conftest.py new file mode 100644 index 0000000..d9c5a96 --- /dev/null +++ b/tests/python/conftest.py @@ -0,0 +1,35 @@ +# +# Copyright 2026 NWChemEx-Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import parallelzone as pz +import pytest + + +@pytest.fixture(scope="session", autouse=True) +def _session_runtime_view(): + """ + Holds a single RuntimeView for the whole pytest session. + + MPI may only be initialized/finalized once per process. The first + RuntimeView constructed owns that responsibility; individual test + modules construct their own RuntimeView per test (e.g. in setUp), + which is safe only as long as this session-scoped instance is still + alive to keep MPI initialized in between. Without this, pytest would + run each test module independently and MPI would be finalized after + the first module's tests finished, breaking every module after it. + """ + rv = pz.runtime.RuntimeView() + yield rv diff --git a/tests/python/unit_tests/test_friendzone.py b/tests/python/unit_tests/test_friendzone.py deleted file mode 100644 index 980b434..0000000 --- a/tests/python/unit_tests/test_friendzone.py +++ /dev/null @@ -1,35 +0,0 @@ -# -# Copyright 2023 NWChemEx-Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -import os -import sys -import unittest - -import parallelzone as pz - -if __name__ == "__main__": - rv = pz.runtime.RuntimeView() - - my_dir = os.path.dirname(os.path.realpath(__file__)) - root_dir = os.path.dirname(os.path.dirname(os.path.dirname(my_dir))) - src_dir = os.path.join(root_dir, "src", "python") - sys.path.append(src_dir) - - loader = unittest.TestLoader() - tests = loader.discover(my_dir) - testrunner = unittest.runner.TextTestRunner() - ret = not testrunner.run(tests).wasSuccessful() - sys.exit(ret) From f081dcf8528db586261ef44ada58dae9a6420189 Mon Sep 17 00:00:00 2001 From: "Ryan M. Richard" Date: Sat, 11 Jul 2026 12:52:07 -0500 Subject: [PATCH 04/22] Fix docs autoapi_dirs stale src/ path; add extra_index_url to build_pypi_dist docs/source/conf.py's autoapi_dirs still pointed at 'src', the pre-move path from before the de-C++-ify commit relocated Python source to python/friendzone -- broke the Sphinx docs build outright. build_pypi_dist's 'Verify Wheel Installs' step needs extra_index_url to resolve nwchemex-simde (TestPyPI-only) when installing the just-built wheel; also add a PR-time dry-run build check (test_pypi_deploy, matching NWXCMake's own pull_request.yaml) to catch sdist-manifest issues an editable install wouldn't. --- .github/workflows/merge.yaml | 2 ++ .github/workflows/pull_request.yaml | 11 +++++++++++ docs/source/conf.py | 2 +- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/.github/workflows/merge.yaml b/.github/workflows/merge.yaml index ce884a6..66a7c08 100644 --- a/.github/workflows/merge.yaml +++ b/.github/workflows/merge.yaml @@ -43,6 +43,8 @@ jobs: steps: - name: Build PyPI Distribution uses: ryanmrichard/.github/.github/actions/build_pypi_dist@master + with: + extra_index_url: https://test.pypi.org/simple/ - name: Publish to PyPI uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/pull_request.yaml b/.github/workflows/pull_request.yaml index b75025a..43ba99a 100644 --- a/.github/workflows/pull_request.yaml +++ b/.github/workflows/pull_request.yaml @@ -24,6 +24,17 @@ jobs: check_formatting: uses: ryanmrichard/.github/.github/workflows/check_formatting.yaml@master + # Dry-run of the actual sdist/wheel build (not just an editable install via + # test_python below) -- catches e.g. files missing from the sdist manifest + # that an editable install wouldn't. Matches NWXCMake's own pull_request.yaml. + test_pypi_deploy: + runs-on: ubuntu-latest + steps: + - name: Build PyPI Distribution + uses: ryanmrichard/.github/.github/actions/build_pypi_dist@master + with: + extra_index_url: https://test.pypi.org/simple/ + test_nwx_docs: uses: ryanmrichard/.github/.github/workflows/test_nwx_docs.yaml@master with: diff --git a/docs/source/conf.py b/docs/source/conf.py index 34aa938..63124e2 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -173,7 +173,7 @@ # -- Extension configuration ------------------------------------------------- autoapi_dirs = [ - "../../src", + "../../python", # '../../tests', ] autoapi_add_toctree_entry = False From 6b23819d245934867abe64699c5c78b92421a94c Mon Sep 17 00:00:00 2001 From: "Ryan M. Richard" Date: Sat, 11 Jul 2026 12:55:26 -0500 Subject: [PATCH 05/22] Re-trigger CI with build_pypi_dist Python-version fix From 0a53a259a728eee1e2ec1223f531b46692349718 Mon Sep 17 00:00:00 2001 From: "Ryan M. Richard" Date: Sat, 11 Jul 2026 13:27:54 -0500 Subject: [PATCH 06/22] Add numpy to the test extra tests/python/unit_tests/nwx2ase/test_nwchem_via_ase.py and nwx2molssi/test_nwchem_via_molssi.py both unconditionally 'import numpy as np' at module level (unlike the ase/qcengine/etc. imports, which are properly feature-gated via find_spec rather than a hard import), so numpy is a real, always-needed test dependency that was missing from pyproject.toml. --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index aa9e7a1..75cd261 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,7 +42,7 @@ dependencies = ["nwchemex-simde"] [project.optional-dependencies] ase = ["ase"] molssi = ["qcengine", "qcelemental", "networkx"] -test = ["pytest"] +test = ["pytest", "numpy"] dev = ["nwchemex-friendzone[test]", "tox", "pre-commit"] # Python source lives under python/ (analogous to cxx/ in C++ repos). From c5b9bfc9cebb6969134bf345cbfbe6d76a8c97df Mon Sep 17 00:00:00 2001 From: "Ryan M. Richard" Date: Sat, 11 Jul 2026 14:00:39 -0500 Subject: [PATCH 07/22] Pin test_python job's Python to 3.12, matching the ecosystem's published wheel target Under actions/setup-python's default '3.x' (resolves to 3.14 today), pip found no matching prebuilt wheel for nwchemex-simde/parallelzone/etc (cp312-only, per platform_matrix.yaml) and fell back to building them from source. That build succeeded (setup_nwx_dev_env provides MPI/ Boost/etc.) but produced a binary missing the rpath-bundled shared libs (e.g. libspdlog) that cibuildwheel+delocate normally bundle into the real published wheel, so it failed to import at runtime. --- .github/workflows/pull_request.yaml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pull_request.yaml b/.github/workflows/pull_request.yaml index 43ba99a..a7ca10a 100644 --- a/.github/workflows/pull_request.yaml +++ b/.github/workflows/pull_request.yaml @@ -57,10 +57,18 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 + # Pinned to 3.12, not "3.x" (whatever's newest, e.g. 3.14 today): + # nwchemex-simde and its own transitive deps are only published as + # prebuilt wheels for cp312 (platform_matrix.yaml's release_matrix). + # Under a newer ambient Python, pip falls back to building those from + # source, which succeeds (setup_nwx_dev_env provides MPI/Boost/etc.) + # but produces a binary missing the rpath-bundled shared libs that + # cibuildwheel+delocate/auditwheel normally provide for the real + # published wheel, so it fails to import at runtime. - name: Set up Python uses: actions/setup-python@v5 with: - python-version: "3.x" + python-version: "3.12" - name: Setup NWX Dev Environment uses: ryanmrichard/.github/.github/actions/setup_nwx_dev_env@master with: From 50d6e3312dd152031bdc9f3683decf11b6486683 Mon Sep 17 00:00:00 2001 From: "Ryan M. Richard" Date: Sun, 12 Jul 2026 12:53:54 -0500 Subject: [PATCH 08/22] TEMPORARY: add diagnostic step to isolate MPI/RuntimeView crash --- .github/workflows/pull_request.yaml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.github/workflows/pull_request.yaml b/.github/workflows/pull_request.yaml index a7ca10a..0551ccb 100644 --- a/.github/workflows/pull_request.yaml +++ b/.github/workflows/pull_request.yaml @@ -77,6 +77,21 @@ jobs: shell: bash run: | pip install -e ".[dev]" --extra-index-url https://test.pypi.org/simple/ + # TEMPORARY diagnostic step: isolate whether the crash happens at + # RuntimeView() construction itself (outside pytest's output + # capturing/buffering, so any native crash message is unambiguous) + # before falling back to guessing from pytest's own captured output. + - name: Debug RuntimeView Construction + shell: bash + run: | + echo "-- ldd on the parallelzone extension --" + site_pkg=$(python -c "import parallelzone, os; print(os.path.dirname(parallelzone.__file__))") + find "$site_pkg" -name "*.so" -exec ldd {} \; + echo "-- importing parallelzone --" + python -u -c "import parallelzone; print('import OK')" + echo "-- constructing RuntimeView --" + python -u -c "import parallelzone as pz; rv = pz.runtime.RuntimeView(); print('RuntimeView OK')" + - name: Test Python shell: bash run: | From 7ce27084364003b866ee93973b86bdb89e6b3db9 Mon Sep 17 00:00:00 2001 From: "Ryan M. Richard" Date: Sun, 12 Jul 2026 13:40:15 -0500 Subject: [PATCH 09/22] TEMPORARY: fetch OpenMPI help text and set OPAL_PREFIX to unmask real MPI_Init error --- .github/workflows/pull_request.yaml | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/.github/workflows/pull_request.yaml b/.github/workflows/pull_request.yaml index 0551ccb..d092d15 100644 --- a/.github/workflows/pull_request.yaml +++ b/.github/workflows/pull_request.yaml @@ -84,12 +84,24 @@ jobs: - name: Debug RuntimeView Construction shell: bash run: | - echo "-- ldd on the parallelzone extension --" - site_pkg=$(python -c "import parallelzone, os; print(os.path.dirname(parallelzone.__file__))") - find "$site_pkg" -name "*.so" -exec ldd {} \; + echo "-- fetching OpenMPI 4.1.6 help text only, to unmask the real error --" + # The wheel's bundled libmpi looks for its runtime data/help text at + # a hardcoded /usr/local/openmpi/... path that doesn't exist here, + # so MPI_Init's *actual* failure reason gets swallowed by a + # secondary "couldn't open the help file" message. OPAL_PREFIX lets + # us point it at a real (if minimal) share/openmpi/ directory so + # the real underlying message prints instead. + curl -fsSL https://download.open-mpi.org/release/open-mpi/v4.1/openmpi-4.1.6.tar.gz -o /tmp/openmpi.tar.gz + mkdir -p /tmp/openmpi-src + tar -xzf /tmp/openmpi.tar.gz -C /tmp/openmpi-src --strip-components=1 + mkdir -p /tmp/opal-prefix/share/openmpi + find /tmp/openmpi-src -iname "help-*" -type f -exec cp {} /tmp/opal-prefix/share/openmpi/ \; + ls /tmp/opal-prefix/share/openmpi/ | head -20 + export OPAL_PREFIX=/tmp/opal-prefix + echo "-- importing parallelzone --" python -u -c "import parallelzone; print('import OK')" - echo "-- constructing RuntimeView --" + echo "-- constructing RuntimeView (with OPAL_PREFIX set) --" python -u -c "import parallelzone as pz; rv = pz.runtime.RuntimeView(); print('RuntimeView OK')" - name: Test Python From e11588494f48e2e11481daa6a9533a91313a9d03 Mon Sep 17 00:00:00 2001 From: "Ryan M. Richard" Date: Sun, 12 Jul 2026 14:21:10 -0500 Subject: [PATCH 10/22] TEMPORARY: extend RuntimeView diagnostics with pip list, ldd hash, MCA shmem probes Extends the zero-rebuild diagnostic step to confirm which nwchemex package versions were actually installed, compare the bundled libmpi hash/size/symbols against round 1 and round 3 builds, and print verbose shmem/opal component selection output plus forced shmem=posix/mmap/sysv attempts, per Part 1 Step 1 of the wheel-MPI debugging plan. --- .github/workflows/pull_request.yaml | 38 ++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pull_request.yaml b/.github/workflows/pull_request.yaml index d092d15..5040041 100644 --- a/.github/workflows/pull_request.yaml +++ b/.github/workflows/pull_request.yaml @@ -99,9 +99,45 @@ jobs: ls /tmp/opal-prefix/share/openmpi/ | head -20 export OPAL_PREFIX=/tmp/opal-prefix + echo "-- installed nwchemex packages --" + pip list | grep -i nwchemex + + echo "-- site-packages .libs dirs --" + SITE_DIR=$(python -c "import site; print(site.getsitepackages()[0])") + for d in "$SITE_DIR"/*.libs; do + echo "== $d ==" + ls -la "$d" || true + done + + echo "-- ldd of parallelzone extension --" + PZ_EXT=$(python -c "import parallelzone, os; print(os.path.dirname(parallelzone.__file__))") + find "$PZ_EXT" -name "*.so*" -exec ldd {} \; + + echo "-- bundled libmpi: hash, symbol count, size --" + LIBMPI=$(find "$SITE_DIR" -path "*.libs/libmpi*" | head -1) + echo "libmpi path: $LIBMPI" + ls -la "$LIBMPI" + nm -D "$LIBMPI" | grep -ci shmem || true + echo "-- importing parallelzone --" python -u -c "import parallelzone; print('import OK')" - echo "-- constructing RuntimeView (with OPAL_PREFIX set) --" + + echo "-- constructing RuntimeView (with OPAL_PREFIX set, verbose shmem/opal) --" + OMPI_MCA_shmem_base_verbose=100 \ + OMPI_MCA_opal_base_verbose=100 \ + OMPI_MCA_opal_base_help_aggregate=0 \ + python -u -c "import parallelzone as pz; rv = pz.runtime.RuntimeView(); print('RuntimeView OK')" || true + + echo "-- forcing shmem=posix --" + OMPI_MCA_shmem=posix python -u -c "import parallelzone as pz; rv = pz.runtime.RuntimeView(); print('RuntimeView OK (posix)')" || true + + echo "-- forcing shmem=mmap --" + OMPI_MCA_shmem=mmap python -u -c "import parallelzone as pz; rv = pz.runtime.RuntimeView(); print('RuntimeView OK (mmap)')" || true + + echo "-- forcing shmem=sysv --" + OMPI_MCA_shmem=sysv python -u -c "import parallelzone as pz; rv = pz.runtime.RuntimeView(); print('RuntimeView OK (sysv)')" || true + + echo "-- final: real (non-forced) attempt, must succeed for step to pass --" python -u -c "import parallelzone as pz; rv = pz.runtime.RuntimeView(); print('RuntimeView OK')" - name: Test Python From df356c258bbf331fa386ac506a8aa233359fb573 Mon Sep 17 00:00:00 2001 From: "Ryan M. Richard" Date: Sun, 12 Jul 2026 14:31:26 -0500 Subject: [PATCH 11/22] Remove TEMPORARY RuntimeView diagnostic step Root cause found: vendored MPI's singleton startup can't find its orted daemon executable (auditwheel/delocate only bundle shared libs, not bin/ executables). Fixed at the source in build_pypi_dist (stop vendoring MPI on Linux, require system MPI), so the diagnostic step is no longer needed. --- .github/workflows/pull_request.yaml | 63 ----------------------------- 1 file changed, 63 deletions(-) diff --git a/.github/workflows/pull_request.yaml b/.github/workflows/pull_request.yaml index 5040041..a7ca10a 100644 --- a/.github/workflows/pull_request.yaml +++ b/.github/workflows/pull_request.yaml @@ -77,69 +77,6 @@ jobs: shell: bash run: | pip install -e ".[dev]" --extra-index-url https://test.pypi.org/simple/ - # TEMPORARY diagnostic step: isolate whether the crash happens at - # RuntimeView() construction itself (outside pytest's output - # capturing/buffering, so any native crash message is unambiguous) - # before falling back to guessing from pytest's own captured output. - - name: Debug RuntimeView Construction - shell: bash - run: | - echo "-- fetching OpenMPI 4.1.6 help text only, to unmask the real error --" - # The wheel's bundled libmpi looks for its runtime data/help text at - # a hardcoded /usr/local/openmpi/... path that doesn't exist here, - # so MPI_Init's *actual* failure reason gets swallowed by a - # secondary "couldn't open the help file" message. OPAL_PREFIX lets - # us point it at a real (if minimal) share/openmpi/ directory so - # the real underlying message prints instead. - curl -fsSL https://download.open-mpi.org/release/open-mpi/v4.1/openmpi-4.1.6.tar.gz -o /tmp/openmpi.tar.gz - mkdir -p /tmp/openmpi-src - tar -xzf /tmp/openmpi.tar.gz -C /tmp/openmpi-src --strip-components=1 - mkdir -p /tmp/opal-prefix/share/openmpi - find /tmp/openmpi-src -iname "help-*" -type f -exec cp {} /tmp/opal-prefix/share/openmpi/ \; - ls /tmp/opal-prefix/share/openmpi/ | head -20 - export OPAL_PREFIX=/tmp/opal-prefix - - echo "-- installed nwchemex packages --" - pip list | grep -i nwchemex - - echo "-- site-packages .libs dirs --" - SITE_DIR=$(python -c "import site; print(site.getsitepackages()[0])") - for d in "$SITE_DIR"/*.libs; do - echo "== $d ==" - ls -la "$d" || true - done - - echo "-- ldd of parallelzone extension --" - PZ_EXT=$(python -c "import parallelzone, os; print(os.path.dirname(parallelzone.__file__))") - find "$PZ_EXT" -name "*.so*" -exec ldd {} \; - - echo "-- bundled libmpi: hash, symbol count, size --" - LIBMPI=$(find "$SITE_DIR" -path "*.libs/libmpi*" | head -1) - echo "libmpi path: $LIBMPI" - ls -la "$LIBMPI" - nm -D "$LIBMPI" | grep -ci shmem || true - - echo "-- importing parallelzone --" - python -u -c "import parallelzone; print('import OK')" - - echo "-- constructing RuntimeView (with OPAL_PREFIX set, verbose shmem/opal) --" - OMPI_MCA_shmem_base_verbose=100 \ - OMPI_MCA_opal_base_verbose=100 \ - OMPI_MCA_opal_base_help_aggregate=0 \ - python -u -c "import parallelzone as pz; rv = pz.runtime.RuntimeView(); print('RuntimeView OK')" || true - - echo "-- forcing shmem=posix --" - OMPI_MCA_shmem=posix python -u -c "import parallelzone as pz; rv = pz.runtime.RuntimeView(); print('RuntimeView OK (posix)')" || true - - echo "-- forcing shmem=mmap --" - OMPI_MCA_shmem=mmap python -u -c "import parallelzone as pz; rv = pz.runtime.RuntimeView(); print('RuntimeView OK (mmap)')" || true - - echo "-- forcing shmem=sysv --" - OMPI_MCA_shmem=sysv python -u -c "import parallelzone as pz; rv = pz.runtime.RuntimeView(); print('RuntimeView OK (sysv)')" || true - - echo "-- final: real (non-forced) attempt, must succeed for step to pass --" - python -u -c "import parallelzone as pz; rv = pz.runtime.RuntimeView(); print('RuntimeView OK')" - - name: Test Python shell: bash run: | From fd7588d777bd5b81ca646ef7f50541c6a1c1167d Mon Sep 17 00:00:00 2001 From: "Ryan M. Richard" Date: Sun, 12 Jul 2026 22:06:52 -0500 Subject: [PATCH 12/22] TEMPORARY: re-add narrow RuntimeView/chemist/tensorwrapper import diagnostic Pytest now dies with no traceback on the first real test (after excluding vendored MPI from wheels and switching to system MPI), right where conftest.py's session RuntimeView fixture would run. Isolate whether RuntimeView() itself is still the crash point against system MPI, or whether it's further downstream in chemist/tensorwrapper. --- .github/workflows/pull_request.yaml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/.github/workflows/pull_request.yaml b/.github/workflows/pull_request.yaml index a7ca10a..80e7ef8 100644 --- a/.github/workflows/pull_request.yaml +++ b/.github/workflows/pull_request.yaml @@ -77,6 +77,25 @@ jobs: shell: bash run: | pip install -e ".[dev]" --extra-index-url https://test.pypi.org/simple/ + # TEMPORARY diagnostic: test_python now excludes vendored MPI libs and + # links against system MPI, but pytest still dies with no traceback on + # the first test (which pulls in conftest.py's session RuntimeView + # fixture) -- isolate whether RuntimeView() itself is still the + # problem (now against system MPI, not the old vendored/orted issue) + # or something further downstream (chemist/tensorwrapper native code). + - name: Debug RuntimeView Construction + shell: bash + run: | + echo "-- ldconfig resolution of libmpi --" + ldconfig -p | grep libmpi || true + which orted || true + echo "-- importing parallelzone --" + python -u -c "import parallelzone; print('import OK')" + echo "-- constructing RuntimeView --" + python -u -c "import parallelzone as pz; rv = pz.runtime.RuntimeView(); print('RuntimeView OK')" + echo "-- importing chemist/tensorwrapper --" + python -u -c "import chemist; print('chemist import OK')" + python -u -c "import tensorwrapper; print('tensorwrapper import OK')" - name: Test Python shell: bash run: | From 6894addede751f0ca1c2189805e22adee4b7fbeb Mon Sep 17 00:00:00 2001 From: "Ryan M. Richard" Date: Sun, 12 Jul 2026 22:48:58 -0500 Subject: [PATCH 13/22] TEMPORARY: expand diagnostic to trace which libmpi.so is actually loaded Confirmed the republished wheel (0.1.61) no longer bundles libmpi, yet RuntimeView() still fails with the same /usr/local/openmpi help-file error as the old vendored-MPI bug. Check whether the system apt libmpi.so.40 itself somehow has that prefix baked in, whether any residual bundled libmpi remains anywhere in site-packages, and the extension's actual RPATH/RUNPATH. --- .github/workflows/pull_request.yaml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/pull_request.yaml b/.github/workflows/pull_request.yaml index 80e7ef8..654031f 100644 --- a/.github/workflows/pull_request.yaml +++ b/.github/workflows/pull_request.yaml @@ -89,6 +89,17 @@ jobs: echo "-- ldconfig resolution of libmpi --" ldconfig -p | grep libmpi || true which orted || true + echo "-- does system libmpi itself contain the /usr/local/openmpi string? --" + strings /lib/x86_64-linux-gnu/libmpi.so.40 | grep -i "usr/local/openmpi\|prefix" | head -10 || true + echo "-- site-packages content check: any leftover libmpi anywhere? --" + SITE_DIR=$(python -c "import site; print(site.getsitepackages()[0])") + find "$SITE_DIR" -iname "*libmpi*" 2>/dev/null || true + find "$SITE_DIR" -iname "*.libs" -type d 2>/dev/null || true + echo "-- pip show nwchemex-parallelzone --" + pip show -f nwchemex-parallelzone | head -30 + echo "-- ldd + readelf RPATH/RUNPATH of parallelzone extension --" + PZ_EXT=$(python -c "import parallelzone, os; print(os.path.dirname(parallelzone.__file__))") + find "$PZ_EXT" -name "*.so*" -exec sh -c 'echo "== {} =="; ldd "{}"; readelf -d "{}" | grep -i "rpath\|runpath"' \; echo "-- importing parallelzone --" python -u -c "import parallelzone; print('import OK')" echo "-- constructing RuntimeView --" From 206673c5a7fd5cb7972eb498e26b76a475f8a07a Mon Sep 17 00:00:00 2001 From: "Ryan M. Richard" Date: Sun, 12 Jul 2026 22:54:54 -0500 Subject: [PATCH 14/22] TEMPORARY: fix broken-pipe in diagnostic, add ompi_info prefix check, scope to ubuntu pip show | head triggered a broken pipe under pipefail, aborting the step before reaching the real checks. Also add a direct ompi_info --path prefix call (independent of our own extension) to check what the ambient apt-installed OpenMPI actually reports, and check the CMake-installed lib/ shared libraries' own RPATH/RUNPATH, not just the wheel's own extension. Scope the whole step to ubuntu-latest only since it uses Linux-only tools (strings, readelf) and was spuriously failing macOS. --- .github/workflows/pull_request.yaml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pull_request.yaml b/.github/workflows/pull_request.yaml index 654031f..12baf71 100644 --- a/.github/workflows/pull_request.yaml +++ b/.github/workflows/pull_request.yaml @@ -84,6 +84,7 @@ jobs: # problem (now against system MPI, not the old vendored/orted issue) # or something further downstream (chemist/tensorwrapper native code). - name: Debug RuntimeView Construction + if: matrix.os == 'ubuntu-latest' shell: bash run: | echo "-- ldconfig resolution of libmpi --" @@ -95,11 +96,18 @@ jobs: SITE_DIR=$(python -c "import site; print(site.getsitepackages()[0])") find "$SITE_DIR" -iname "*libmpi*" 2>/dev/null || true find "$SITE_DIR" -iname "*.libs" -type d 2>/dev/null || true - echo "-- pip show nwchemex-parallelzone --" - pip show -f nwchemex-parallelzone | head -30 + echo "-- pip show nwchemex-parallelzone (no head, avoid broken pipe) --" + pip show -f nwchemex-parallelzone > /tmp/pip_show.txt 2>&1 || true + head -20 /tmp/pip_show.txt + echo "-- what prefix does the ambient ompi_info report? --" + ompi_info --path prefix || true + ompi_info --path sysconfdir || true echo "-- ldd + readelf RPATH/RUNPATH of parallelzone extension --" PZ_EXT=$(python -c "import parallelzone, os; print(os.path.dirname(parallelzone.__file__))") find "$PZ_EXT" -name "*.so*" -exec sh -c 'echo "== {} =="; ldd "{}"; readelf -d "{}" | grep -i "rpath\|runpath"' \; + echo "-- readelf RPATH/RUNPATH of the shared lib/ dir (libparallelzone.so etc) --" + LIB_DIR=$(python -c "import site; print(site.getsitepackages()[0])")/lib + find "$LIB_DIR" -name "*.so*" -exec sh -c 'echo "== {} =="; readelf -d "{}" | grep -i "rpath\|runpath\|needed.*mpi"' \; 2>/dev/null || true echo "-- importing parallelzone --" python -u -c "import parallelzone; print('import OK')" echo "-- constructing RuntimeView --" From 8a04bdac555d127b171df31ceffafde635207887 Mon Sep 17 00:00:00 2001 From: "Ryan M. Richard" Date: Sun, 12 Jul 2026 23:00:06 -0500 Subject: [PATCH 15/22] Remove TEMPORARY RuntimeView/RPATH diagnostic step Confirmed root cause chain end to end: vendored MPI's singleton mode couldn't find its orted daemon (auditwheel never bundles bin/ executables) -> fixed by excluding MPI libs from the wheel and requiring system MPI -> the auditwheel --exclude glob patterns silently didn't work (cibuildwheel v2.22.0's pinned auditwheel 6.1.0 only supports exact soname matching, not fnmatch) -> fixed with exact sonames. Confirmed via ompi_info/readelf that the extension's baked-in RUNPATH still lists the build container's /usr/local/openmpi/lib first, but correctly falls through to the system libmpi.so.40 since that directory doesn't exist at runtime -- RuntimeView() and chemist/tensorwrapper imports all succeed now. --- .github/workflows/pull_request.yaml | 38 ----------------------------- 1 file changed, 38 deletions(-) diff --git a/.github/workflows/pull_request.yaml b/.github/workflows/pull_request.yaml index 12baf71..a7ca10a 100644 --- a/.github/workflows/pull_request.yaml +++ b/.github/workflows/pull_request.yaml @@ -77,44 +77,6 @@ jobs: shell: bash run: | pip install -e ".[dev]" --extra-index-url https://test.pypi.org/simple/ - # TEMPORARY diagnostic: test_python now excludes vendored MPI libs and - # links against system MPI, but pytest still dies with no traceback on - # the first test (which pulls in conftest.py's session RuntimeView - # fixture) -- isolate whether RuntimeView() itself is still the - # problem (now against system MPI, not the old vendored/orted issue) - # or something further downstream (chemist/tensorwrapper native code). - - name: Debug RuntimeView Construction - if: matrix.os == 'ubuntu-latest' - shell: bash - run: | - echo "-- ldconfig resolution of libmpi --" - ldconfig -p | grep libmpi || true - which orted || true - echo "-- does system libmpi itself contain the /usr/local/openmpi string? --" - strings /lib/x86_64-linux-gnu/libmpi.so.40 | grep -i "usr/local/openmpi\|prefix" | head -10 || true - echo "-- site-packages content check: any leftover libmpi anywhere? --" - SITE_DIR=$(python -c "import site; print(site.getsitepackages()[0])") - find "$SITE_DIR" -iname "*libmpi*" 2>/dev/null || true - find "$SITE_DIR" -iname "*.libs" -type d 2>/dev/null || true - echo "-- pip show nwchemex-parallelzone (no head, avoid broken pipe) --" - pip show -f nwchemex-parallelzone > /tmp/pip_show.txt 2>&1 || true - head -20 /tmp/pip_show.txt - echo "-- what prefix does the ambient ompi_info report? --" - ompi_info --path prefix || true - ompi_info --path sysconfdir || true - echo "-- ldd + readelf RPATH/RUNPATH of parallelzone extension --" - PZ_EXT=$(python -c "import parallelzone, os; print(os.path.dirname(parallelzone.__file__))") - find "$PZ_EXT" -name "*.so*" -exec sh -c 'echo "== {} =="; ldd "{}"; readelf -d "{}" | grep -i "rpath\|runpath"' \; - echo "-- readelf RPATH/RUNPATH of the shared lib/ dir (libparallelzone.so etc) --" - LIB_DIR=$(python -c "import site; print(site.getsitepackages()[0])")/lib - find "$LIB_DIR" -name "*.so*" -exec sh -c 'echo "== {} =="; readelf -d "{}" | grep -i "rpath\|runpath\|needed.*mpi"' \; 2>/dev/null || true - echo "-- importing parallelzone --" - python -u -c "import parallelzone; print('import OK')" - echo "-- constructing RuntimeView --" - python -u -c "import parallelzone as pz; rv = pz.runtime.RuntimeView(); print('RuntimeView OK')" - echo "-- importing chemist/tensorwrapper --" - python -u -c "import chemist; print('chemist import OK')" - python -u -c "import tensorwrapper; print('tensorwrapper import OK')" - name: Test Python shell: bash run: | From b75a843481033fb7648b95f0496b23dc843817f0 Mon Sep 17 00:00:00 2001 From: "Ryan M. Richard" Date: Mon, 13 Jul 2026 14:08:01 -0500 Subject: [PATCH 16/22] Fix NWChem-via-MolSSI module key casing regression, enable real NWChem in CI load_nwchem_via_molssi_modules hardcoded its module keys to lowercase "nwchem" instead of "NWChem" (regressed in a226a7d, "Python Packaging (#30)"), diverging from this function's own docstring, the sibling ASE wrapper's "ASE(NWChem) : " convention, and both this repo's and NWChemEx's test suites, all of which expect "NWChem : SCF" etc. Since ModuleManager key lookup is exact-string, this silently broke every NWChem-via-MolSSI module registration. Also enable the new needs_nwchem install in test_python so this repo's own nwx2molssi tests (and NWChemEx's downstream compute_energy tests) actually exercise a real NWChem install in CI instead of skipping via is_nwchem_enabled(), which was masking this regression from CI in the first place. --- .github/workflows/pull_request.yaml | 4 ++++ python/friendzone/nwx2molssi/nwchem_via_molssi.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pull_request.yaml b/.github/workflows/pull_request.yaml index a7ca10a..df1d07b 100644 --- a/.github/workflows/pull_request.yaml +++ b/.github/workflows/pull_request.yaml @@ -73,6 +73,10 @@ jobs: uses: ryanmrichard/.github/.github/actions/setup_nwx_dev_env@master with: compiler: gcc-14 + needs_nwchem: "true" + - name: Sanity-check NWChem Is On PATH + shell: bash + run: which nwchem - name: Install shell: bash run: | diff --git a/python/friendzone/nwx2molssi/nwchem_via_molssi.py b/python/friendzone/nwx2molssi/nwchem_via_molssi.py index 124b250..ef82619 100644 --- a/python/friendzone/nwx2molssi/nwchem_via_molssi.py +++ b/python/friendzone/nwx2molssi/nwchem_via_molssi.py @@ -124,7 +124,7 @@ def load_nwchem_via_molssi_modules(mm): """ if is_nwchem_enabled(): for method in ["SCF", "B3LYP", "MP2", "CCSD", "CCSD(T)"]: - egy_key = "nwchem" + " : " + method + egy_key = "NWChem" + " : " + method grad_key = egy_key + " Gradient" mm.add_module(egy_key, QCEngineEnergy()) mm.add_module(grad_key, QCEngineGradient()) From 974049d218248d56767026ab78e4965917f7d7cf Mon Sep 17 00:00:00 2001 From: "Ryan M. Richard" Date: Mon, 13 Jul 2026 15:16:56 -0500 Subject: [PATCH 17/22] test_python: install molssi+ase extras so real NWChem gets exercised needs_nwchem alone installs the external nwchem binary, but load_molssi_modules()/load_ase_modules() are no-ops unless qcengine/qcelemental/networkx (molssi extra) and ase (ase extra) are also installed -- confirmed the smoke-test PR's first run still skipped every nwx2molssi/nwx2ase NWChem test with 'MolSSI/ASE is not enabled!' despite the casing fix and a real nwchem on PATH. --- .github/workflows/pull_request.yaml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pull_request.yaml b/.github/workflows/pull_request.yaml index df1d07b..7bec260 100644 --- a/.github/workflows/pull_request.yaml +++ b/.github/workflows/pull_request.yaml @@ -80,7 +80,13 @@ jobs: - name: Install shell: bash run: | - pip install -e ".[dev]" --extra-index-url https://test.pypi.org/simple/ + # molssi/ase are optional friends (extra deps only needed if the + # corresponding external program is present); install them + # explicitly here since needs_nwchem above means this job now has + # a real NWChem to exercise both wrapper paths against, rather + # than everything just no-op'ing via is_molssi_enabled()/ + # is_ase_enabled(). + pip install -e ".[dev,molssi,ase]" --extra-index-url https://test.pypi.org/simple/ - name: Test Python shell: bash run: | From f6d0cb4ce2c33967307cbd967ebfb5b6ae10a2ad Mon Sep 17 00:00:00 2001 From: "Ryan M. Richard" Date: Mon, 13 Jul 2026 22:10:36 -0500 Subject: [PATCH 18/22] pytest: ignore ase's own numpy 2.5 shape-deprecation warning ase's Atoms.new_array() still does `a.shape = (-1,) + shape` on an empty array, deprecated by numpy 2.5 in favor of np.reshape. Only surfaced now that the ase extra is actually installed and exercised in CI (previously silently skipped via is_ase_enabled()). Unrelated to anything on our side -- ase's own bug, filtered out rather than letting it hard-fail every ASE-backed friend test under this repo's filterwarnings = ["error", ...] pytest config. --- pyproject.toml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 75cd261..dcc6aea 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,5 +57,14 @@ minversion = "8.0" addopts = ["-ra", "--showlocals", "--strict-markers", "--strict-config"] xfail_strict = true log_cli_level = "INFO" -filterwarnings = ["error", "ignore::pytest.PytestCacheWarning"] +filterwarnings = [ + "error", + "ignore::pytest.PytestCacheWarning", + # ase's own Atoms.new_array (ase/atoms.py) still does `a.shape = (-1,) + # + shape` on an empty array, which numpy >=2.5 deprecates in favor of + # np.reshape. Unrelated to anything NWChemEx-side; ase's own bug, not + # ours to fix -- ignore rather than let it hard-fail every ASE-backed + # friend test. + "ignore:Setting the shape on a NumPy array has been deprecated:DeprecationWarning:ase\\..*", +] testpaths = ["tests"] From 740082cf59852747af8a23658f93622b675754a3 Mon Sep 17 00:00:00 2001 From: "Ryan M. Richard" Date: Wed, 15 Jul 2026 10:13:37 -0500 Subject: [PATCH 19/22] use fork --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index dcc6aea..2a1c973 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,7 +37,7 @@ classifiers = [ "Programming Language :: Python :: 3.14", ] dynamic = ["version"] -dependencies = ["nwchemex-simde"] +dependencies = ["nwchemex-simde @ git+https://github.com/ryanmrichard/SimDE"] [project.optional-dependencies] ase = ["ase"] From 7c59b969ff2ee31c77f342857e89b8571815e139 Mon Sep 17 00:00:00 2001 From: "Ryan M. Richard" Date: Mon, 3 Aug 2026 12:16:45 -0500 Subject: [PATCH 20/22] add pre-commit to gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 23c79ec..e87a55f 100644 --- a/.gitignore +++ b/.gitignore @@ -80,3 +80,6 @@ uuid.db # This is a generated file # src/python/friendzone/friends.py + +# Automatically installed in dev mode +.pre-commit-config.yaml From a8e00458c37a1853b8eccaf60881861f5d6a9712 Mon Sep 17 00:00:00 2001 From: "Ryan M. Richard" Date: Wed, 5 Aug 2026 10:32:58 -0500 Subject: [PATCH 21/22] update nightly --- .github/workflows/nightly.yaml | 11 ++- .github/workflows/pull_request.yaml | 75 +--------------- .github/workflows/test_python_build.yaml | 105 +++++++++++++++++++++++ 3 files changed, 116 insertions(+), 75 deletions(-) create mode 100644 .github/workflows/test_python_build.yaml diff --git a/.github/workflows/nightly.yaml b/.github/workflows/nightly.yaml index 51a975b..b0dcec3 100644 --- a/.github/workflows/nightly.yaml +++ b/.github/workflows/nightly.yaml @@ -14,13 +14,16 @@ # name: Nightly Workflow +# Re-runs the same build/test jobs pull_request.yaml runs, on a schedule, to +# catch breakage that originates outside the repo (upstream dependency +# releases, refreshed GitHub runner images) rather than from a code change. +# Both callers share test_python_build.yaml, so there is nothing to keep in +# sync by hand. on: schedule: - cron: "0 6 * * *" # Every day at 06:00 UTC (00:00 CST) jobs: - test_library: - uses: NWChemEx/.github/.github/workflows/test_nwx_library.yaml@master - with: - compilers: '["gcc-14", "clang-18"]' + test_python_build: + uses: ./.github/workflows/test_python_build.yaml diff --git a/.github/workflows/pull_request.yaml b/.github/workflows/pull_request.yaml index 7bec260..097eb47 100644 --- a/.github/workflows/pull_request.yaml +++ b/.github/workflows/pull_request.yaml @@ -24,79 +24,12 @@ jobs: check_formatting: uses: ryanmrichard/.github/.github/workflows/check_formatting.yaml@master - # Dry-run of the actual sdist/wheel build (not just an editable install via - # test_python below) -- catches e.g. files missing from the sdist manifest - # that an editable install wouldn't. Matches NWXCMake's own pull_request.yaml. - test_pypi_deploy: - runs-on: ubuntu-latest - steps: - - name: Build PyPI Distribution - uses: ryanmrichard/.github/.github/actions/build_pypi_dist@master - with: - extra_index_url: https://test.pypi.org/simple/ - test_nwx_docs: uses: ryanmrichard/.github/.github/workflows/test_nwx_docs.yaml@master with: doc_target: "Sphinx" - # FriendZone is pure Python (no CMakeLists.txt), so there's nothing to - # compile and the compiler axis of the shared dev matrix is meaningless - # here -- just a lean OS-only matrix. setup_nwx_dev_env still runs (via its - # unconditional MPI/Boost install) because nwchemex-simde's prebuilt wheel - # dynamically links against libmpi at import time, and bare GitHub runners - # have no MPI by default. - test_python: - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest, macos-latest] - runs-on: ${{ matrix.os }} - steps: - - name: Checkout Source - uses: actions/checkout@v4 - with: - fetch-depth: 0 - # Pinned to 3.12, not "3.x" (whatever's newest, e.g. 3.14 today): - # nwchemex-simde and its own transitive deps are only published as - # prebuilt wheels for cp312 (platform_matrix.yaml's release_matrix). - # Under a newer ambient Python, pip falls back to building those from - # source, which succeeds (setup_nwx_dev_env provides MPI/Boost/etc.) - # but produces a binary missing the rpath-bundled shared libs that - # cibuildwheel+delocate/auditwheel normally provide for the real - # published wheel, so it fails to import at runtime. - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - name: Setup NWX Dev Environment - uses: ryanmrichard/.github/.github/actions/setup_nwx_dev_env@master - with: - compiler: gcc-14 - needs_nwchem: "true" - - name: Sanity-check NWChem Is On PATH - shell: bash - run: which nwchem - - name: Install - shell: bash - run: | - # molssi/ase are optional friends (extra deps only needed if the - # corresponding external program is present); install them - # explicitly here since needs_nwchem above means this job now has - # a real NWChem to exercise both wrapper paths against, rather - # than everything just no-op'ing via is_molssi_enabled()/ - # is_ase_enabled(). - pip install -e ".[dev,molssi,ase]" --extra-index-url https://test.pypi.org/simple/ - - name: Test Python - shell: bash - run: | - set +e - pytest -v - pytest_exit=$? - set -e - - if [ "$pytest_exit" -eq 5 ]; then - echo "::error::zero pytest tests were found." - exit 1 - fi - exit "$pytest_exit" + # Shared with nightly.yaml -- see that file's comments for why this repo + # doesn't use the shared test_nwx_cmake_build/test_nwx_pip_build workflows. + test_python_build: + uses: ./.github/workflows/test_python_build.yaml diff --git a/.github/workflows/test_python_build.yaml b/.github/workflows/test_python_build.yaml new file mode 100644 index 0000000..69da0d4 --- /dev/null +++ b/.github/workflows/test_python_build.yaml @@ -0,0 +1,105 @@ +# Copyright 2026 NWChemEx-Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +name: Test Python Build +# Builds FriendZone's distribution and runs its test suite. pull_request.yaml +# and nightly.yaml both need exactly these jobs, so they live here instead of +# being duplicated in each. +# +# This is a repo-local reusable workflow rather than one of the shared +# NWChemEx/.github ones because FriendZone is pure Python (no CMakeLists.txt): +# test_nwx_cmake_build/test_nwx_pip_build both drive a scikit-build-core CMake +# build and don't apply. It isn't a composite action because the duplication +# includes the job's matrix and runs-on, which an action can't own. +# +# Callers reference it as `uses: ./.github/workflows/test_python_build.yaml`, +# which resolves at the caller's own commit -- so a PR that edits this file +# tests the edited version. + +on: + workflow_call: + +jobs: + # Dry-run of the actual sdist/wheel build (not just an editable install via + # test_python below) -- catches e.g. files missing from the sdist manifest + # that an editable install wouldn't. Matches NWXCMake's own pull_request.yaml. + test_pypi_deploy: + runs-on: ubuntu-latest + steps: + - name: Build PyPI Distribution + uses: ryanmrichard/.github/.github/actions/build_pypi_dist@master + with: + extra_index_url: https://test.pypi.org/simple/ + + # FriendZone is pure Python (no CMakeLists.txt), so there's nothing to + # compile and the compiler axis of the shared dev matrix is meaningless + # here -- just a lean OS-only matrix. setup_nwx_dev_env still runs (via its + # unconditional MPI/Boost install) because nwchemex-simde's prebuilt wheel + # dynamically links against libmpi at import time, and bare GitHub runners + # have no MPI by default. + test_python: + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest] + runs-on: ${{ matrix.os }} + steps: + - name: Checkout Source + uses: actions/checkout@v4 + with: + fetch-depth: 0 + # Pinned to 3.12, not "3.x" (whatever's newest, e.g. 3.14 today): + # nwchemex-simde and its own transitive deps are only published as + # prebuilt wheels for cp312 (platform_matrix.yaml's release_matrix). + # Under a newer ambient Python, pip falls back to building those from + # source, which succeeds (setup_nwx_dev_env provides MPI/Boost/etc.) + # but produces a binary missing the rpath-bundled shared libs that + # cibuildwheel+delocate/auditwheel normally provide for the real + # published wheel, so it fails to import at runtime. + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + - name: Setup NWX Dev Environment + uses: ryanmrichard/.github/.github/actions/setup_nwx_dev_env@master + with: + compiler: gcc-14 + needs_nwchem: "true" + - name: Sanity-check NWChem Is On PATH + shell: bash + run: which nwchem + - name: Install + shell: bash + run: | + # molssi/ase are optional friends (extra deps only needed if the + # corresponding external program is present); install them + # explicitly here since needs_nwchem above means this job now has + # a real NWChem to exercise both wrapper paths against, rather + # than everything just no-op'ing via is_molssi_enabled()/ + # is_ase_enabled(). + pip install -e ".[dev,molssi,ase]" --extra-index-url https://test.pypi.org/simple/ + - name: Test Python + shell: bash + run: | + set +e + pytest -v + pytest_exit=$? + set -e + + if [ "$pytest_exit" -eq 5 ]; then + echo "::error::zero pytest tests were found." + exit 1 + fi + exit "$pytest_exit" From f2852c749c00b997922989cc8d63677bd83155cf Mon Sep 17 00:00:00 2001 From: "Ryan M. Richard" Date: Fri, 7 Aug 2026 09:14:40 -0500 Subject: [PATCH 22/22] Migrate CI/CD to NWChemEx org, master branch, real PyPI - Repoint all ryanmrichard/.github references to NWChemEx/.github - Flip merge.yaml/pull_request.yaml triggers from build_overhaul to master - Drop TestPyPI repository-url/extra_index_url everywhere, including the repo-local test_python_build.yaml (simde is now on real PyPI, so the extra index is no longer needed to resolve it) - Pin nwchemex-simde to its published PyPI floor instead of a git URL --- .github/workflows/merge.yaml | 10 ++++------ .github/workflows/pull_request.yaml | 6 +++--- .github/workflows/test_python_build.yaml | 8 +++----- pyproject.toml | 2 +- 4 files changed, 11 insertions(+), 15 deletions(-) diff --git a/.github/workflows/merge.yaml b/.github/workflows/merge.yaml index 66a7c08..20437ef 100644 --- a/.github/workflows/merge.yaml +++ b/.github/workflows/merge.yaml @@ -18,15 +18,15 @@ name: Merge Workflow on: push: branches: - - build_overhaul + - master jobs: tag-commit: - uses: ryanmrichard/.github/.github/workflows/tag.yaml@master + uses: NWChemEx/.github/.github/workflows/tag.yaml@master secrets: inherit deploy_nwx_docs: - uses: ryanmrichard/.github/.github/workflows/deploy_nwx_docs.yaml@master + uses: NWChemEx/.github/.github/workflows/deploy_nwx_docs.yaml@master with: doc_target: "Sphinx" secrets: inherit @@ -42,12 +42,10 @@ jobs: id-token: write steps: - name: Build PyPI Distribution - uses: ryanmrichard/.github/.github/actions/build_pypi_dist@master + uses: NWChemEx/.github/.github/actions/build_pypi_dist@master with: - extra_index_url: https://test.pypi.org/simple/ - name: Publish to PyPI uses: pypa/gh-action-pypi-publish@release/v1 with: packages-dir: dist/ - repository-url: https://test.pypi.org/legacy/ diff --git a/.github/workflows/pull_request.yaml b/.github/workflows/pull_request.yaml index 097eb47..5abd8c9 100644 --- a/.github/workflows/pull_request.yaml +++ b/.github/workflows/pull_request.yaml @@ -18,14 +18,14 @@ name: Pull Request Workflow on: pull_request: branches: - - build_overhaul + - master jobs: check_formatting: - uses: ryanmrichard/.github/.github/workflows/check_formatting.yaml@master + uses: NWChemEx/.github/.github/workflows/check_formatting.yaml@master test_nwx_docs: - uses: ryanmrichard/.github/.github/workflows/test_nwx_docs.yaml@master + uses: NWChemEx/.github/.github/workflows/test_nwx_docs.yaml@master with: doc_target: "Sphinx" diff --git a/.github/workflows/test_python_build.yaml b/.github/workflows/test_python_build.yaml index 69da0d4..72a4ba1 100644 --- a/.github/workflows/test_python_build.yaml +++ b/.github/workflows/test_python_build.yaml @@ -39,9 +39,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Build PyPI Distribution - uses: ryanmrichard/.github/.github/actions/build_pypi_dist@master - with: - extra_index_url: https://test.pypi.org/simple/ + uses: NWChemEx/.github/.github/actions/build_pypi_dist@master # FriendZone is pure Python (no CMakeLists.txt), so there's nothing to # compile and the compiler axis of the shared dev matrix is meaningless @@ -73,7 +71,7 @@ jobs: with: python-version: "3.12" - name: Setup NWX Dev Environment - uses: ryanmrichard/.github/.github/actions/setup_nwx_dev_env@master + uses: NWChemEx/.github/.github/actions/setup_nwx_dev_env@master with: compiler: gcc-14 needs_nwchem: "true" @@ -89,7 +87,7 @@ jobs: # a real NWChem to exercise both wrapper paths against, rather # than everything just no-op'ing via is_molssi_enabled()/ # is_ase_enabled(). - pip install -e ".[dev,molssi,ase]" --extra-index-url https://test.pypi.org/simple/ + pip install -e ".[dev,molssi,ase]" - name: Test Python shell: bash run: | diff --git a/pyproject.toml b/pyproject.toml index 2a1c973..3fafe29 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,7 +37,7 @@ classifiers = [ "Programming Language :: Python :: 3.14", ] dynamic = ["version"] -dependencies = ["nwchemex-simde @ git+https://github.com/ryanmrichard/SimDE"] +dependencies = ["nwchemex-simde>=0.0.77"] [project.optional-dependencies] ase = ["ase"]