Skip to content

Commit

Permalink
Merge pull request #4 from Liam-Deacon/py311-support
Browse files Browse the repository at this point in the history
Add Python 3.11 support for pip install
  • Loading branch information
Liam-Deacon authored Jan 13, 2024
2 parents c7fe278 + df18b3f commit 13ea576
Show file tree
Hide file tree
Showing 12 changed files with 420 additions and 83 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ MANIFEST
# cache
*.pyc
**/__pycache__
_skbuild/

# built binary objects
*.so
Expand Down
101 changes: 101 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# Modified from https://numpy.org/doc/stable/f2py/buildtools/skbuild.html#cmake-modules-only

### setup project ###
cmake_minimum_required(VERSION 3.9)

project(phaseshifts
VERSION 0.1.7
DESCRIPTION "LIBPHSH module"
LANGUAGES C Fortran
)

# Safety net
if(PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR)
message(
FATAL_ERROR
"In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there.\n"
)
endif()

# Ensure scikit-build modules
if (NOT SKBUILD)
find_package(PythonInterp 3.12 REQUIRED)
# If skbuild is not the driver; include its utilities in CMAKE_MODULE_PATH
execute_process(
COMMAND "${PYTHON_EXECUTABLE}"
-c "import os, skbuild; print(os.path.dirname(skbuild.__file__))"
OUTPUT_VARIABLE SKBLD_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE
)
list(APPEND CMAKE_MODULE_PATH "${SKBLD_DIR}/resources/cmake")
message(STATUS "Looking in ${SKBLD_DIR}/resources/cmake for CMake modules")
endif()

# scikit-build style includes
find_package(PythonExtensions REQUIRED) # for ${PYTHON_EXTENSION_MODULE_SUFFIX}

# Grab the variables from a local Python installation
# NumPy headers
execute_process(
COMMAND "${PYTHON_EXECUTABLE}"
-c "import numpy; print(numpy.get_include())"
OUTPUT_VARIABLE NumPy_INCLUDE_DIRS
OUTPUT_STRIP_TRAILING_WHITESPACE
)
# F2PY headers
execute_process(
COMMAND "${PYTHON_EXECUTABLE}"
-c "import numpy.f2py; print(numpy.f2py.get_include())"
OUTPUT_VARIABLE F2PY_INCLUDE_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE
)

# Prepping the module
set(f2py_module_name "libphsh")
set(f2py_module_dirpath "phaseshifts/lib")
set(fortran_src_file "${CMAKE_SOURCE_DIR}/${f2py_module_dirpath}/${f2py_module_name}.f")
set(f2py_module_c "${f2py_module_name}module.c")
set(f2py_wrapper_f "${f2py_module_name}-f2pywrappers.f")

# Target for enforcing dependencies
add_custom_target(genpyf
DEPENDS "${fortran_src_file}"
)
add_custom_command(
OUTPUT "${f2py_module_c}" "${f2py_wrapper_f}"
COMMAND ${PYTHON_EXECUTABLE} -m "numpy.f2py"
-m "${f2py_module_name}"
--lower # Important
"${fortran_src_file}"
DEPENDS "${fortran_src_file}" # Fortran source
)

add_library("${f2py_module_name}" MODULE
"${f2py_wrapper_f}"
"${f2py_module_c}"
"${F2PY_INCLUDE_DIR}/fortranobject.c"
"${fortran_src_file}")

target_include_directories("${f2py_module_name}" PUBLIC
${F2PY_INCLUDE_DIR}
${NumPy_INCLUDE_DIRS}
${PYTHON_INCLUDE_DIRS})

set_target_properties("${f2py_module_name}" PROPERTIES SUFFIX "${PYTHON_EXTENSION_MODULE_SUFFIX}")
set_target_properties("${f2py_module_name}" PROPERTIES PREFIX "")



if (UNIX)
if (APPLE)
set_target_properties("${f2py_module_name}" PROPERTIES
LINK_FLAGS '-Wl,-dylib,-undefined,dynamic_lookup')
else()
set_target_properties("${f2py_module_name}" PROPERTIES
LINK_FLAGS '-Wl,--allow-shlib-undefined')
endif()
endif()

add_dependencies("${f2py_module_name}" genpyf)

install(TARGETS "${f2py_module_name}" DESTINATION phaseshifts/lib/)
5 changes: 4 additions & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,7 @@ recursive-include phaseshifts/plugins *.py *.txt *.rst
recursive-include phaseshifts/lib *.f *.c *.cpp *.h *.for *.py *.pyx
recursive-exclude phaseshifts/build *
recursive-exclude phaseshifts/dist *
recursive-include phaseshifts/res *
recursive-include phaseshifts/res *

# TODO: Remove shared libraries from source distribution includes once numpy.distutils migration is stable
recursive-include phaseshifts/lib *.so *.pyd
43 changes: 39 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,40 @@
wheel: deps
NPY_DISABLE_CPU_FEATURES="AVX512F AVX512_SKX" python setup.py build bdist_wheel
# Makefile for assisting with some common development activities

deps:
pip install wheel numpy setuptools
PYTHON ?= $(shell pyenv which python 2>/dev/null || echo python)

.PHONY: wheel install-deps libphsh check test clean

#: Quickly generate binary wheel
wheel: install-deps
$(PYTHON) setup.py build bdist_wheel

#: Install dependencies
install-deps:
$(PYTHON) -m pip install wheel numpy setuptools meson ninja pytest scikit-build

#: Install library into current virtualenv
install:
$(PYTHON) -m pip install .

libphsh.cmake:
cmake -S . -B build \
-DPYTHON_INCLUDE_DIR="$$($(PYTHON) -c "import sysconfig; print(sysconfig.get_path('include'))")" \
-DPYTHON_LIBRARY="$$($(PYTHON) -c "import sysconfig; print(sysconfig.get_config_var('LIBDIR'))")"
FFLAGS="-fallow-argument-mismatch -std=f95" \
cmake --build build

#: Build the f2py wrapped libphsh shared library within source tree
libphsh:
$(PYTHON) setup.py build_ext --inplace

#: Perform checks
check: libphsh
$(PYTHON) -m pytest tests/

test: check

#: Remove any artifacts
clean:
rm -rf build dist _skbuild \
phaseshifts/lib/libphshmodule.c phaseshifts/lib/libphsh-f2pywrappers.f \
phaseshifts/lib/libphsh*.so phaseshifts/lib/libphsh*.pyd
14 changes: 13 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,20 @@ information please read the documentation at `<http://pythonhosted.org//phaseshi
Install
=======

TDLR;
-----

For python 3.11 or older::

pip install wheel numpy setuptools
pip install -e .
phsh --help

Details
-------

The `phaseshifts <http://https://pypi.python.org/pypi/phaseshifts/>`_ package
requires CPython 2.6 or later and also uses the `numpy
requires CPython 2.7 or later and also uses the `numpy
<http://www.scipy.org/scipylib/download.html>`_, `scipy
<http://www.scipy.org/scipylib/download.html>`_ and `periodictable
<http://https://pypi.python.org/pypi/periodictable>`_ packages.
Expand Down
13 changes: 3 additions & 10 deletions phaseshifts/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,4 @@
from pkg_resources import get_distribution, DistributionNotFound
__project__ = "phaseshifts"
__version__ = "0.1.7"

__project__ = 'phaseshifts'
__version__ = None # required for initial installation

try:
__version__ = get_distribution(__project__).version
except DistributionNotFound:
VERSION = __project__ + '-' + '(local)'
else:
VERSION = __project__ + '-' + __version__
VERSION = __project__ + "-" + __version__
6 changes: 6 additions & 0 deletions phaseshifts/lib/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
"""The subpackage is for library code, likely compiled from Fortran or C sources."""
from ._fortran_lib import is_module_importable, compile_f2py_shared_library, FORTRAN_LIBS, LIBPHSH_MODULE

# NOTE: Attempt to compile libphsh library on import if not already available, useful when installing from source dist
if not is_module_importable(LIBPHSH_MODULE):
compile_f2py_shared_library(**FORTRAN_LIBS[LIBPHSH_MODULE])
59 changes: 59 additions & 0 deletions phaseshifts/lib/_fortran_lib.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
"""This module can be used for checking whether Fortran wrapped libraries such as `libphsh` hve compiled successfully.
Notes
-----
The functionality currently targets `libphsh.f` by default and provides utilities to check for the
compiled wrapped `libphsh` fortran shared library and adds the ability to crudely compile
on the fly (useful when installing this package from source distribution).
"""
import importlib
import os
import subprocess
import sys
from typing import Optional

LIBPHSH_MODULE = "phaseshifts.lib.libphsh"

FORTRAN_LIBS = {
LIBPHSH_MODULE: {"source": "libphsh.f", "module_name": LIBPHSH_MODULE},
}


def is_module_importable(module: str) -> bool:
"""Determine whether `module` is importable."""
try:
is_importable = bool(importlib.import_module(module))
except ModuleNotFoundError:
is_importable = False
return is_importable


def compile_f2py_shared_library(
source: str,
module_name: Optional[str] = None,
**f2py_kwargs,
) -> int:
"""Compile `source` into f2py wrapped shared library given by `module_name`.
Examples
--------
>>> compile_f2py_shared_library(**FORTRAN_LIBS[LIBPHSH_MODULE])
See Also
--------
numpy.f2py
"""
return subprocess.check_call(
[
sys.executable,
"-m",
"numpy.f2py",
source,
"-m",
module_name or os.path.basename(source),
"-c",
*[f"--{key}={val!r}" for key, val in f2py_kwargs.items()],
],
cwd=os.path.dirname(__file__),
)
Loading

0 comments on commit 13ea576

Please sign in to comment.