From b5ce558698eb011f2cdcf27bb5b7a2ecb2de52de Mon Sep 17 00:00:00 2001 From: Garrett Barter Date: Sun, 7 Jan 2024 10:03:14 -0700 Subject: [PATCH 01/17] more robust build --- .github/workflows/Publish_pyFrame3DD.yml | 31 +++-- pyproject.toml | 2 +- setup.py | 157 +++++++++++------------ 3 files changed, 93 insertions(+), 97 deletions(-) diff --git a/.github/workflows/Publish_pyFrame3DD.yml b/.github/workflows/Publish_pyFrame3DD.yml index c02e8d0..7077f47 100644 --- a/.github/workflows/Publish_pyFrame3DD.yml +++ b/.github/workflows/Publish_pyFrame3DD.yml @@ -30,10 +30,11 @@ jobs: env: CC: '${{ steps.install_cc.outputs.cc }}' CXX: '${{ steps.install_cc.outputs.cxx }}' - uses: pypa/cibuildwheel@v2.16.1 + uses: pypa/cibuildwheel@v2.16.2 - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: + name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }} path: ./wheelhouse/*.whl build_sdist: @@ -45,27 +46,33 @@ jobs: - name: Build sdist run: pipx run build --sdist - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: + name: cibw-sdist path: dist/*.tar.gz upload_pypi: needs: [build_wheels, build_sdist] runs-on: ubuntu-latest + environment: pypi + permissions: + id-token: write # IMPORTANT: this permission is mandatory for trusted publishing # upload to PyPI on every tag starting with 'v' #if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') # alternatively, to publish when a GitHub Release is created, use the following rule: if: github.event_name == 'release' && github.event.action == 'published' steps: - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: - # unpacks default artifact into dist/ - # if `name: artifact` is omitted, the action will create extra parent dir - name: artifact + # unpacks all CIBW artifacts into dist/ + pattern: cibw-* path: dist + merge-multiple: true + + - name: Upload to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + #with: + # user: __token__ + # password: ${{ secrets.pypi_password }} + # # To test: repository_url: https://test.pypi.org/legacy/ - - uses: pypa/gh-action-pypi-publish@v1.8.10 - with: - user: __token__ - password: ${{ secrets.pypi_password }} - # To test: repository_url: https://test.pypi.org/legacy/ diff --git a/pyproject.toml b/pyproject.toml index 5b69139..b5ffed2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -125,5 +125,5 @@ atomic = true #src_paths=isort,test [tool.cibuildwheel] -skip = ["cp36-*", "cp37-*", "cp38-*", "*-win32", "*arm64", "pp*"] +skip = ["cp36-*", "cp37-*", "cp38-*", "*-win32"] build-frontend = "build" diff --git a/setup.py b/setup.py index c0e1d0b..98240e8 100644 --- a/setup.py +++ b/setup.py @@ -1,14 +1,10 @@ #!/usr/bin/env python -# encoding: utf-8 - -# setup.py -# only if building in place: ``python setup.py build_ext --inplace`` import os import re -import platform import shutil +import platform import setuptools -import subprocess +from setuptools.command.build_ext import build_ext ####### # This forces wheels to be platform specific @@ -25,93 +21,86 @@ class BinaryDistribution(Distribution): def has_ext_modules(foo): return True ####### - - -def run_meson_build(staging_dir): - prefix = os.path.join(os.getcwd(), staging_dir) - purelibdir = "." - - # check if meson extra args are specified - meson_args = "" - if "MESON_ARGS" in os.environ: - meson_args = os.environ["MESON_ARGS"] - # A weird add-on on mac github action runners needs to be removed - if meson_args.find("buildtype") >= 0: meson_args = "" - - if platform.system() == "Windows": - if not "FC" in os.environ: - os.environ["FC"] = "gfortran" - if not "CC" in os.environ: - os.environ["CC"] = "gcc" - - # configure - meson_path = shutil.which("meson") - if meson_path is None: - raise OSError("The meson command cannot be found on the system") - - meson_call = [meson_path, "setup", staging_dir, "--wipe", - f"--prefix={prefix}", f"-Dpython.purelibdir={purelibdir}", - f"-Dpython.platlibdir={purelibdir}", meson_args] - meson_call = [m for m in meson_call if m != ""] - print(meson_call) - p1 = subprocess.run(meson_call, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - os.makedirs(staging_dir, exist_ok=True) - setup_log = os.path.join(staging_dir, "setup.log") - with open(setup_log, "wb") as f: - f.write(p1.stdout) - if p1.returncode != 0: - with open(setup_log, "r") as f: - print(f.read()) - raise OSError(meson_call, f"The meson setup command failed! Check the log at {setup_log} for more information.") - - # build - meson_call = [meson_path, "compile", "-vC", staging_dir] - meson_call = [m for m in meson_call if m != ""] - print(meson_call) - p2 = subprocess.run(meson_call, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - compile_log = os.path.join(staging_dir, "compile.log") - with open(compile_log, "wb") as f: - f.write(p2.stdout) - if p2.returncode != 0: - with open(compile_log, "r") as f: - print(f.read()) - raise OSError(meson_call, f"The meson compile command failed! Check the log at {compile_log} for more information.") - +this_dir = os.path.abspath(os.path.dirname(__file__)) +build_dir = os.path.join(this_dir, "build") def copy_shared_libraries(): - build_path = os.path.join(staging_dir, "pyframe3dd") + build_path = os.path.join(build_dir, "pyframe3dd") for root, _dirs, files in os.walk(build_path): for file in files: - # move pyframe3dd to just under staging_dir - if file.endswith((".so", ".lib", ".pyd", ".pdb", ".dylib", ".dll", ".mod")): + # move pyframe3dd libraries to just under staging_dir + if file.endswith((".so", ".lib", ".pyd", ".pdb", ".dylib", ".dll")): if ".so.p" in root or ".pyd.p" in root: # excludes intermediate object files continue file_path = os.path.join(root, file) new_path = str(file_path) - match = re.search(staging_dir, new_path) + match = re.search(build_dir, new_path) new_path = new_path[match.span()[1] + 1 :] print(f"Copying build file {file_path} -> {new_path}") - shutil.move(file_path, new_path) - + shutil.copy(file_path, new_path) +####### +class MesonExtension(setuptools.Extension): + + def __init__(self, name, sourcedir="", **kwa): + setuptools.Extension.__init__(self, name, sources=[], **kwa) + self.sourcedir = os.path.abspath(sourcedir) + +class MesonBuildExt(build_ext): + + def copy_extensions_to_source(self): + newext = [] + for ext in self.extensions: + if isinstance(ext, MesonExtension): continue + newext.append( ext ) + self.extensions = newext + super().copy_extensions_to_source() + + def build_extension(self, ext): + if not isinstance(ext, MesonExtension): + super().build_extension(ext) + + else: + + # Ensure that Meson is present and working + try: + self.spawn(["meson", "--version"]) + except OSError: + raise RuntimeError("Cannot find meson executable") + + # check if meson extra args are specified + meson_args = "" + if "MESON_ARGS" in os.environ: + meson_args = os.environ["MESON_ARGS"] + + if platform.system() == "Windows": + if "FC" not in os.environ: + os.environ["FC"] = "gfortran" + if "CC" not in os.environ: + os.environ["CC"] = "gcc" + + purelibdir = "." + configure_call = ["meson", "setup", build_dir, "--wipe", + f"-Dpython.purelibdir={purelibdir}", f"--prefix={build_dir}", + f"-Dpython.platlibdir={purelibdir}"] + meson_args.split() + configure_call = [m for m in configure_call if m.strip() != ""] + print(configure_call) + + build_call = ["meson", "compile", "-vC", build_dir] + print(build_call) + + self.build_temp = build_dir + + # Need fresh build directory for CMake + os.makedirs(self.build_temp, exist_ok=True) + + self.spawn(configure_call) + self.spawn(build_call) + copy_shared_libraries() + + if __name__ == "__main__": - # This is where the meson build system will install to, it is then - # used as the sources for setuptools - staging_dir = "meson_build" - - # this keeps the meson build system from running more than once - if "dist" not in str(os.path.abspath(__file__)): - cwd = os.getcwd() - run_meson_build(staging_dir) - os.chdir(cwd) - copy_shared_libraries() - - init_file = os.path.join("pyframe3dd", "__init__.py") - #__version__ = re.findall( - # r"""__version__ = ["']+([0-9\.]*)["']+""", - # open(init_file).read(), - #)[0] - - setuptools.setup(cmdclass={'bdist_wheel': bdist_wheel}, distclass=BinaryDistribution) - -#os.environ['NPY_DISTUTILS_APPEND_FLAGS'] = '1' + setuptools.setup(cmdclass={"bdist_wheel": bdist_wheel, "build_ext": MesonBuildExt}, + distclass=BinaryDistribution, + ext_modules=[ MesonExtension("pyframe3dd", this_dir) ], + ) From 9fc71ce8f31f4c7fe1e2bd51b10fe344ce063d89 Mon Sep 17 00:00:00 2001 From: Garrett Barter Date: Sun, 7 Jan 2024 10:19:51 -0700 Subject: [PATCH 02/17] let meson create directory --- setup.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/setup.py b/setup.py index 98240e8..3114a8b 100644 --- a/setup.py +++ b/setup.py @@ -91,9 +91,6 @@ def build_extension(self, ext): self.build_temp = build_dir - # Need fresh build directory for CMake - os.makedirs(self.build_temp, exist_ok=True) - self.spawn(configure_call) self.spawn(build_call) copy_shared_libraries() From 5d3aab896a555b77c746b2f2972b4e4c19399060 Mon Sep 17 00:00:00 2001 From: Garrett Barter Date: Sun, 7 Jan 2024 10:35:31 -0700 Subject: [PATCH 03/17] try this one --- setup.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 3114a8b..bef3a75 100644 --- a/setup.py +++ b/setup.py @@ -22,13 +22,13 @@ def has_ext_modules(foo): return True ####### this_dir = os.path.abspath(os.path.dirname(__file__)) +staging_dir = os.path.join(this_dir, "meson_build") build_dir = os.path.join(this_dir, "build") def copy_shared_libraries(): build_path = os.path.join(build_dir, "pyframe3dd") for root, _dirs, files in os.walk(build_path): for file in files: - # move pyframe3dd libraries to just under staging_dir if file.endswith((".so", ".lib", ".pyd", ".pdb", ".dylib", ".dll")): if ".so.p" in root or ".pyd.p" in root: # excludes intermediate object files continue @@ -80,19 +80,23 @@ def build_extension(self, ext): os.environ["CC"] = "gcc" purelibdir = "." - configure_call = ["meson", "setup", build_dir, "--wipe", + configure_call = ["meson", "setup", staging_dir, "--wipe", f"-Dpython.purelibdir={purelibdir}", f"--prefix={build_dir}", f"-Dpython.platlibdir={purelibdir}"] + meson_args.split() configure_call = [m for m in configure_call if m.strip() != ""] print(configure_call) - build_call = ["meson", "compile", "-vC", build_dir] + build_call = ["meson", "compile", "-vC", staging_dir] print(build_call) + install_call = ["meson", "install", "-C", staging_dir] + print(install_call) + self.build_temp = build_dir self.spawn(configure_call) self.spawn(build_call) + self.spawn(install_call) copy_shared_libraries() From 2f4b5d9cc30aa96c0ea567f739d7e52195b4ebec Mon Sep 17 00:00:00 2001 From: Garrett Barter Date: Sun, 7 Jan 2024 10:45:07 -0700 Subject: [PATCH 04/17] sigh debug --- .github/workflows/CI_pyFrame3DD.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/CI_pyFrame3DD.yml b/.github/workflows/CI_pyFrame3DD.yml index 7b3e12d..3de803d 100644 --- a/.github/workflows/CI_pyFrame3DD.yml +++ b/.github/workflows/CI_pyFrame3DD.yml @@ -43,6 +43,12 @@ jobs: run: | '${{ steps.cp.outputs.python-path }}' -m pip install -vv .[test] + - name: Setup tmate session + uses: mxschmitt/action-tmate@v3 + with: + detached: true + if: contains( matrix.os, 'ubuntu') + - name: Test run run: | '${{ steps.cp.outputs.python-path }}' -m pytest test @@ -93,12 +99,6 @@ jobs: run: | conda list printenv - - #- name: Setup tmate session - # uses: mxschmitt/action-tmate@v3 - # with: - # detached: true - # if: contains( matrix.os, 'windows') # Install - name: Conda Install pyFrame3DD From 778405bf5cfbb5c62268c2a16e3c2529e4807c21 Mon Sep 17 00:00:00 2001 From: Garrett Barter Date: Sun, 7 Jan 2024 10:58:53 -0700 Subject: [PATCH 05/17] hopefully this time --- .github/workflows/CI_pyFrame3DD.yml | 10 +++++----- setup.py | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/CI_pyFrame3DD.yml b/.github/workflows/CI_pyFrame3DD.yml index 3de803d..26966f2 100644 --- a/.github/workflows/CI_pyFrame3DD.yml +++ b/.github/workflows/CI_pyFrame3DD.yml @@ -43,11 +43,11 @@ jobs: run: | '${{ steps.cp.outputs.python-path }}' -m pip install -vv .[test] - - name: Setup tmate session - uses: mxschmitt/action-tmate@v3 - with: - detached: true - if: contains( matrix.os, 'ubuntu') + #- name: Setup tmate session + # uses: mxschmitt/action-tmate@v3 + # with: + # detached: true + # if: contains( matrix.os, 'ubuntu') - name: Test run run: | diff --git a/setup.py b/setup.py index bef3a75..6c4e8bf 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ def has_ext_modules(foo): build_dir = os.path.join(this_dir, "build") def copy_shared_libraries(): - build_path = os.path.join(build_dir, "pyframe3dd") + build_path = os.path.join(staging_dir, "pyframe3dd") for root, _dirs, files in os.walk(build_path): for file in files: if file.endswith((".so", ".lib", ".pyd", ".pdb", ".dylib", ".dll")): @@ -34,7 +34,7 @@ def copy_shared_libraries(): continue file_path = os.path.join(root, file) new_path = str(file_path) - match = re.search(build_dir, new_path) + match = re.search(staging_dir, new_path) new_path = new_path[match.span()[1] + 1 :] print(f"Copying build file {file_path} -> {new_path}") shutil.copy(file_path, new_path) From 0da29c288ef0f21e066d09b7c416cb2c90fa43b7 Mon Sep 17 00:00:00 2001 From: Garrett Barter Date: Sun, 7 Jan 2024 11:11:15 -0700 Subject: [PATCH 06/17] test for windows --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 6c4e8bf..ab44066 100644 --- a/setup.py +++ b/setup.py @@ -81,7 +81,7 @@ def build_extension(self, ext): purelibdir = "." configure_call = ["meson", "setup", staging_dir, "--wipe", - f"-Dpython.purelibdir={purelibdir}", f"--prefix={build_dir}", + f"-Dpython.purelibdir={purelibdir}", f"--prefix={staging_dir}", f"-Dpython.platlibdir={purelibdir}"] + meson_args.split() configure_call = [m for m in configure_call if m.strip() != ""] print(configure_call) @@ -96,7 +96,7 @@ def build_extension(self, ext): self.spawn(configure_call) self.spawn(build_call) - self.spawn(install_call) + #self.spawn(install_call) copy_shared_libraries() From 8d0fb38005f49ad99c0eca7e3fddad6c40876ae2 Mon Sep 17 00:00:00 2001 From: Garrett Barter Date: Sun, 7 Jan 2024 17:21:58 -0700 Subject: [PATCH 07/17] reverting back to old setup.py --- setup.py | 154 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 82 insertions(+), 72 deletions(-) diff --git a/setup.py b/setup.py index ab44066..c0e1d0b 100644 --- a/setup.py +++ b/setup.py @@ -1,10 +1,14 @@ #!/usr/bin/env python +# encoding: utf-8 + +# setup.py +# only if building in place: ``python setup.py build_ext --inplace`` import os import re -import shutil import platform +import shutil import setuptools -from setuptools.command.build_ext import build_ext +import subprocess ####### # This forces wheels to be platform specific @@ -21,15 +25,65 @@ class BinaryDistribution(Distribution): def has_ext_modules(foo): return True ####### -this_dir = os.path.abspath(os.path.dirname(__file__)) -staging_dir = os.path.join(this_dir, "meson_build") -build_dir = os.path.join(this_dir, "build") + + +def run_meson_build(staging_dir): + prefix = os.path.join(os.getcwd(), staging_dir) + purelibdir = "." + + # check if meson extra args are specified + meson_args = "" + if "MESON_ARGS" in os.environ: + meson_args = os.environ["MESON_ARGS"] + # A weird add-on on mac github action runners needs to be removed + if meson_args.find("buildtype") >= 0: meson_args = "" + + if platform.system() == "Windows": + if not "FC" in os.environ: + os.environ["FC"] = "gfortran" + if not "CC" in os.environ: + os.environ["CC"] = "gcc" + + # configure + meson_path = shutil.which("meson") + if meson_path is None: + raise OSError("The meson command cannot be found on the system") + + meson_call = [meson_path, "setup", staging_dir, "--wipe", + f"--prefix={prefix}", f"-Dpython.purelibdir={purelibdir}", + f"-Dpython.platlibdir={purelibdir}", meson_args] + meson_call = [m for m in meson_call if m != ""] + print(meson_call) + p1 = subprocess.run(meson_call, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + os.makedirs(staging_dir, exist_ok=True) + setup_log = os.path.join(staging_dir, "setup.log") + with open(setup_log, "wb") as f: + f.write(p1.stdout) + if p1.returncode != 0: + with open(setup_log, "r") as f: + print(f.read()) + raise OSError(meson_call, f"The meson setup command failed! Check the log at {setup_log} for more information.") + + # build + meson_call = [meson_path, "compile", "-vC", staging_dir] + meson_call = [m for m in meson_call if m != ""] + print(meson_call) + p2 = subprocess.run(meson_call, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + compile_log = os.path.join(staging_dir, "compile.log") + with open(compile_log, "wb") as f: + f.write(p2.stdout) + if p2.returncode != 0: + with open(compile_log, "r") as f: + print(f.read()) + raise OSError(meson_call, f"The meson compile command failed! Check the log at {compile_log} for more information.") + def copy_shared_libraries(): build_path = os.path.join(staging_dir, "pyframe3dd") for root, _dirs, files in os.walk(build_path): for file in files: - if file.endswith((".so", ".lib", ".pyd", ".pdb", ".dylib", ".dll")): + # move pyframe3dd to just under staging_dir + if file.endswith((".so", ".lib", ".pyd", ".pdb", ".dylib", ".dll", ".mod")): if ".so.p" in root or ".pyd.p" in root: # excludes intermediate object files continue file_path = os.path.join(root, file) @@ -37,71 +91,27 @@ def copy_shared_libraries(): match = re.search(staging_dir, new_path) new_path = new_path[match.span()[1] + 1 :] print(f"Copying build file {file_path} -> {new_path}") - shutil.copy(file_path, new_path) + shutil.move(file_path, new_path) + -####### -class MesonExtension(setuptools.Extension): - - def __init__(self, name, sourcedir="", **kwa): - setuptools.Extension.__init__(self, name, sources=[], **kwa) - self.sourcedir = os.path.abspath(sourcedir) - -class MesonBuildExt(build_ext): - - def copy_extensions_to_source(self): - newext = [] - for ext in self.extensions: - if isinstance(ext, MesonExtension): continue - newext.append( ext ) - self.extensions = newext - super().copy_extensions_to_source() - - def build_extension(self, ext): - if not isinstance(ext, MesonExtension): - super().build_extension(ext) - - else: - - # Ensure that Meson is present and working - try: - self.spawn(["meson", "--version"]) - except OSError: - raise RuntimeError("Cannot find meson executable") - - # check if meson extra args are specified - meson_args = "" - if "MESON_ARGS" in os.environ: - meson_args = os.environ["MESON_ARGS"] - - if platform.system() == "Windows": - if "FC" not in os.environ: - os.environ["FC"] = "gfortran" - if "CC" not in os.environ: - os.environ["CC"] = "gcc" - - purelibdir = "." - configure_call = ["meson", "setup", staging_dir, "--wipe", - f"-Dpython.purelibdir={purelibdir}", f"--prefix={staging_dir}", - f"-Dpython.platlibdir={purelibdir}"] + meson_args.split() - configure_call = [m for m in configure_call if m.strip() != ""] - print(configure_call) - - build_call = ["meson", "compile", "-vC", staging_dir] - print(build_call) - - install_call = ["meson", "install", "-C", staging_dir] - print(install_call) - - self.build_temp = build_dir - - self.spawn(configure_call) - self.spawn(build_call) - #self.spawn(install_call) - copy_shared_libraries() - - if __name__ == "__main__": - setuptools.setup(cmdclass={"bdist_wheel": bdist_wheel, "build_ext": MesonBuildExt}, - distclass=BinaryDistribution, - ext_modules=[ MesonExtension("pyframe3dd", this_dir) ], - ) + # This is where the meson build system will install to, it is then + # used as the sources for setuptools + staging_dir = "meson_build" + + # this keeps the meson build system from running more than once + if "dist" not in str(os.path.abspath(__file__)): + cwd = os.getcwd() + run_meson_build(staging_dir) + os.chdir(cwd) + copy_shared_libraries() + + init_file = os.path.join("pyframe3dd", "__init__.py") + #__version__ = re.findall( + # r"""__version__ = ["']+([0-9\.]*)["']+""", + # open(init_file).read(), + #)[0] + + setuptools.setup(cmdclass={'bdist_wheel': bdist_wheel}, distclass=BinaryDistribution) + +#os.environ['NPY_DISTUTILS_APPEND_FLAGS'] = '1' From 2dba82cada4ce398d45dd44546c86b0651e6ff0a Mon Sep 17 00:00:00 2001 From: Garrett Barter Date: Mon, 8 Jan 2024 04:52:57 -0700 Subject: [PATCH 08/17] better for windows? --- setup.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/setup.py b/setup.py index ab44066..6dd340d 100644 --- a/setup.py +++ b/setup.py @@ -28,14 +28,10 @@ def has_ext_modules(foo): def copy_shared_libraries(): build_path = os.path.join(staging_dir, "pyframe3dd") for root, _dirs, files in os.walk(build_path): - for file in files: - if file.endswith((".so", ".lib", ".pyd", ".pdb", ".dylib", ".dll")): - if ".so.p" in root or ".pyd.p" in root: # excludes intermediate object files - continue - file_path = os.path.join(root, file) - new_path = str(file_path) - match = re.search(staging_dir, new_path) - new_path = new_path[match.span()[1] + 1 :] + for f in files: + if f.endswith((".so", ".lib", ".pyd", ".pdb", ".dylib", ".dll")): + file_path = os.path.join(root, f) + new_path = str(file_path).replace(staging_dir + os.sep, "") print(f"Copying build file {file_path} -> {new_path}") shutil.copy(file_path, new_path) From d34420d5a1fce7a2870c5a47176dd7480642ee5e Mon Sep 17 00:00:00 2001 From: Garrett Barter Date: Mon, 8 Jan 2024 04:58:51 -0700 Subject: [PATCH 09/17] cleanup --- setup.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/setup.py b/setup.py index 6dd340d..8e18791 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,5 @@ #!/usr/bin/env python import os -import re import shutil import platform import setuptools @@ -85,14 +84,10 @@ def build_extension(self, ext): build_call = ["meson", "compile", "-vC", staging_dir] print(build_call) - install_call = ["meson", "install", "-C", staging_dir] - print(install_call) - self.build_temp = build_dir self.spawn(configure_call) self.spawn(build_call) - #self.spawn(install_call) copy_shared_libraries() From 065f61915e435c6a731e35ce2936b73739575a2e Mon Sep 17 00:00:00 2001 From: Garrett Barter Date: Wed, 17 Jan 2024 06:08:59 -0700 Subject: [PATCH 10/17] try this out --- meson.build | 7 +-- pyframe3dd/meson.build | 14 ++++-- pyproject.toml | 36 ++++++---------- setup.py | 98 ------------------------------------------ 4 files changed, 25 insertions(+), 130 deletions(-) delete mode 100644 setup.py diff --git a/meson.build b/meson.build index 322e666..8566733 100644 --- a/meson.build +++ b/meson.build @@ -24,12 +24,7 @@ endif # https://mesonbuild.com/Python-module.html # Here we differentiate from the python used by meson, py3_command, and that python target, py3_target. This is useful # when cross compiling like on conda-forge -py_mod = import('python') -if get_option('python_target') != '' - py3 = py_mod.find_installation(get_option('python_target')) -else - py3 = py_mod.find_installation('python') -endif +py3 = import('python').find_installation(pure: false) py3_dep = py3.dependency() message(py3.path()) diff --git a/pyframe3dd/meson.build b/pyframe3dd/meson.build index 2d7e310..bcb71be 100644 --- a/pyframe3dd/meson.build +++ b/pyframe3dd/meson.build @@ -1,3 +1,12 @@ +python_sources = [ + '__init__.py', + 'pyframe3dd.py', +] + +py3.install_sources( + python_sources, + subdir: 'pyframe3dd', +) sources = ['src/py_main.c', 'src/py_structs.h', @@ -19,9 +28,8 @@ sources = ['src/py_main.c', # We need to build a shared library NOT A PYTHON EXTENSION # The ctypes wrapper handles the extension part. # If the interface was done purely in C, then need the python extension. -shared_library('_pyframe3dd', sources, +temp = shared_library('_pyframe3dd', sources, name_prefix: '', include_directories: 'src', - dependencies : py3_dep, + install_dir: py3.get_install_dir() / 'pyframe3dd', install : true) - diff --git a/pyproject.toml b/pyproject.toml index b5ffed2..0106273 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,10 +1,10 @@ [build-system] -requires = ["setuptools", "meson>=1.1", "ninja", "wheel"] -build-backend = "setuptools.build_meta" +requires = ["numpy", "ninja", "meson>=1.1", "meson-python", "wheel"] +build-backend = "mesonpy" [project] name = "WISDEM-pyFrame3DD" -version = "1.0.1" +version = "1.1.1" description = "Python bindings to Frame3DD, a code for static and dynamic structural analysis of 2D and 3D frames and trusses, with permission from Prof Henri Gavin" readme = "README.md" requires-python = ">=3.9" @@ -36,8 +36,14 @@ classifiers = [ # Optional "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Fortran", + "Programming Language :: C", + "Operating System :: Microsoft :: Windows", + "Operating System :: POSIX :: Linux", + "Operating System :: POSIX", + "Operating System :: Unix", + "Operating System :: MacOS", ] dependencies = [ @@ -70,28 +76,12 @@ test = ["pytest"] "Homepage" = "https://github.com/WISDEM/pyFrame3DD" "Project" = "https://frame3dd.sourceforge.net" -# This is configuration specific to the `setuptools` build backend. -# If you are using a different build backend, you will need to change this. -[tool.setuptools] -zip-safe = false -include-package-data = true - -#[tool.setuptools.packages] -#find = {} - -[tool.setuptools.packages.find] -#where = ["wisdem"] -exclude = ["test", "examples"] -namespaces = true - -[tool.setuptools.package-data] -# If there are data files included in your packages that need to be -# installed, specify them here. -"*" = ["*.so", "*.lib", "*.pyd", "*.pdb", "*.dylib", "*.dll"] +[tool.meson-python.args] +install = ['--tags=runtime,python-runtime,bin'] [tool.black] line-length = 120 -target-version = ['py39'] +target-version = ['py311'] include = '\.pyi?$' exclude = ''' /( diff --git a/setup.py b/setup.py deleted file mode 100644 index 8e18791..0000000 --- a/setup.py +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/env python -import os -import shutil -import platform -import setuptools -from setuptools.command.build_ext import build_ext - -####### -# This forces wheels to be platform specific -from setuptools.dist import Distribution -from wheel.bdist_wheel import bdist_wheel as _bdist_wheel - -class bdist_wheel(_bdist_wheel): - def finalize_options(self): - _bdist_wheel.finalize_options(self) - self.root_is_pure = False - -class BinaryDistribution(Distribution): - """Distribution which always forces a binary package with platform name""" - def has_ext_modules(foo): - return True -####### -this_dir = os.path.abspath(os.path.dirname(__file__)) -staging_dir = os.path.join(this_dir, "meson_build") -build_dir = os.path.join(this_dir, "build") - -def copy_shared_libraries(): - build_path = os.path.join(staging_dir, "pyframe3dd") - for root, _dirs, files in os.walk(build_path): - for f in files: - if f.endswith((".so", ".lib", ".pyd", ".pdb", ".dylib", ".dll")): - file_path = os.path.join(root, f) - new_path = str(file_path).replace(staging_dir + os.sep, "") - print(f"Copying build file {file_path} -> {new_path}") - shutil.copy(file_path, new_path) - -####### -class MesonExtension(setuptools.Extension): - - def __init__(self, name, sourcedir="", **kwa): - setuptools.Extension.__init__(self, name, sources=[], **kwa) - self.sourcedir = os.path.abspath(sourcedir) - -class MesonBuildExt(build_ext): - - def copy_extensions_to_source(self): - newext = [] - for ext in self.extensions: - if isinstance(ext, MesonExtension): continue - newext.append( ext ) - self.extensions = newext - super().copy_extensions_to_source() - - def build_extension(self, ext): - if not isinstance(ext, MesonExtension): - super().build_extension(ext) - - else: - - # Ensure that Meson is present and working - try: - self.spawn(["meson", "--version"]) - except OSError: - raise RuntimeError("Cannot find meson executable") - - # check if meson extra args are specified - meson_args = "" - if "MESON_ARGS" in os.environ: - meson_args = os.environ["MESON_ARGS"] - - if platform.system() == "Windows": - if "FC" not in os.environ: - os.environ["FC"] = "gfortran" - if "CC" not in os.environ: - os.environ["CC"] = "gcc" - - purelibdir = "." - configure_call = ["meson", "setup", staging_dir, "--wipe", - f"-Dpython.purelibdir={purelibdir}", f"--prefix={staging_dir}", - f"-Dpython.platlibdir={purelibdir}"] + meson_args.split() - configure_call = [m for m in configure_call if m.strip() != ""] - print(configure_call) - - build_call = ["meson", "compile", "-vC", staging_dir] - print(build_call) - - self.build_temp = build_dir - - self.spawn(configure_call) - self.spawn(build_call) - copy_shared_libraries() - - -if __name__ == "__main__": - setuptools.setup(cmdclass={"bdist_wheel": bdist_wheel, "build_ext": MesonBuildExt}, - distclass=BinaryDistribution, - ext_modules=[ MesonExtension("pyframe3dd", this_dir) ], - ) From 47d4639ab2e20e43eb012dbc5120aa7af3717abf Mon Sep 17 00:00:00 2001 From: Garrett Barter Date: Wed, 17 Jan 2024 10:59:26 -0700 Subject: [PATCH 11/17] more robust name finding --- meson.build | 2 -- pyframe3dd/__init__.py | 3 +- pyframe3dd/pyframe3dd.py | 64 +++++++++++++++++++++++++--------------- pyproject.toml | 1 + 4 files changed, 43 insertions(+), 27 deletions(-) diff --git a/meson.build b/meson.build index 8566733..bee62cc 100644 --- a/meson.build +++ b/meson.build @@ -31,5 +31,3 @@ message(py3.path()) message(py3.get_install_dir()) subdir('pyframe3dd') - - diff --git a/pyframe3dd/__init__.py b/pyframe3dd/__init__.py index 3439576..8f76d42 100644 --- a/pyframe3dd/__init__.py +++ b/pyframe3dd/__init__.py @@ -1,4 +1,5 @@ from .pyframe3dd import Frame, StaticLoadCase, NodeData, ReactionData, ElementData, Options -#import frame3dd + + diff --git a/pyframe3dd/pyframe3dd.py b/pyframe3dd/pyframe3dd.py index 53298f4..36d45af 100644 --- a/pyframe3dd/pyframe3dd.py +++ b/pyframe3dd/pyframe3dd.py @@ -12,25 +12,43 @@ import math from ctypes import POINTER, c_int, c_double, Structure, pointer from collections import namedtuple +import platform import os -#from distutils.sysconfig import get_config_var - -from sys import platform - -libext = None #get_config_var('EXT_SUFFIX') -if libext is None or libext == '': - if platform == "linux" or platform == "linux2": - libext = '.so' - elif platform == "darwin": - libext = '.dylib' - #libext = '.so' - elif platform == "win32": - libext = '.dll' - #libext = '.pyd' - elif platform == "cygwin": - libext = '.dll' - -libname = '_pyframe3dd' + libext +import sysconfig +import sys + +if platform.system() == "Windows": + lib_ext = ".dll" +elif platform.system() == "Darwin": + lib_ext = ".dylib" +else: + lib_ext = ".so" + +libname = '_pyframe3dd' + lib_ext + +pyframe3dd_dir = os.path.dirname( os.path.abspath(__file__) ) + +lib_opt = [os.path.join(pyframe3dd_dir, libname), # pip installs (regular and editable) + os.path.join(os.path.dirname( os.path.dirname( pyframe3dd_dir )), "local", "lib", libname), # WEIS library + os.path.join(os.path.dirname( sysconfig.get_path('stdlib') ), libname), # conda installs + os.path.join(os.path.dirname( sysconfig.get_path('stdlib') ), "lib", libname), # conda installs + os.path.join(os.path.dirname( sysconfig.get_path('stdlib') ), "Library", "lib", libname), # conda installs + os.path.join( sysconfig.get_path('platlib'), "pyframe3dd", "lib", libname), # system-wide pip installs + os.path.join( sysconfig.get_config_var("userbase"), "lib", "python", "site-packages", "pyframe3dd", libname), # system wide local + ] + +for p in sys.meta_path: + if "_build_path" in p.__dict__: + lib_opt += [os.path.join(p._build_path, "pyframe3dd", libname)] + +lib_path = None +for p in lib_opt: + if os.path.exists(p): + lib_path = str(p) + break + +if lib_path is None: + raise Exception(f"Cannot find {libname} in {lib_opt}") c_int_p = POINTER(c_int) c_double_p = POINTER(c_double) @@ -446,12 +464,10 @@ def __init__(self, nodes, reactions, elements, options): # load c module - mydir = os.path.dirname(os.path.realpath(__file__)) # get path to this file - try: - self._pyframe3dd = np.ctypeslib.load_library(libname, mydir) - except: - mydir = os.path.abspath(os.path.dirname(mydir)) - self._pyframe3dd = np.ctypeslib.load_library(libname, mydir) + #mydir = impresources.files(pyframe3dd) + #with impresources.as_file(mydir) as f: + # print(f) + self._pyframe3dd = np.ctypeslib.load_library(libname, os.path.dirname(lib_path)) self._pyframe3dd.run.argtypes = [POINTER(C_Nodes), POINTER(C_Reactions), POINTER(C_Elements), POINTER(C_OtherElementData), c_int, POINTER(C_LoadCase), diff --git a/pyproject.toml b/pyproject.toml index 0106273..229e5d7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -77,6 +77,7 @@ test = ["pytest"] "Project" = "https://frame3dd.sourceforge.net" [tool.meson-python.args] +setup = ['--python.install-env=auto'] install = ['--tags=runtime,python-runtime,bin'] [tool.black] From aaa4c15231ab09e6e11cf869e6789e5f7837e963 Mon Sep 17 00:00:00 2001 From: Garrett Barter Date: Wed, 17 Jan 2024 10:59:41 -0700 Subject: [PATCH 12/17] build options --- .github/workflows/CI_pyFrame3DD.yml | 5 ++--- environment.yml | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/CI_pyFrame3DD.yml b/.github/workflows/CI_pyFrame3DD.yml index 26966f2..bd6aa8f 100644 --- a/.github/workflows/CI_pyFrame3DD.yml +++ b/.github/workflows/CI_pyFrame3DD.yml @@ -37,11 +37,10 @@ jobs: - name: Pip Install pyFrame3DD env: - MESON_ARGS: -Dpython_target=${{ steps.cp.outputs.python-path }} CC: '${{ steps.install_cc.outputs.cc }}' CXX: '${{ steps.install_cc.outputs.cxx }}' run: | - '${{ steps.cp.outputs.python-path }}' -m pip install -vv .[test] + '${{ steps.cp.outputs.python-path }}' -m pip install -v .[test] #- name: Setup tmate session # uses: mxschmitt/action-tmate@v3 @@ -105,7 +104,7 @@ jobs: env: MESON_ARGS: "" run: | - python -m pip install . + python -m pip install . -v - name: Test run run: | diff --git a/environment.yml b/environment.yml index 7274e6b..be8f8df 100644 --- a/environment.yml +++ b/environment.yml @@ -4,9 +4,9 @@ channels: dependencies: - meson + - meson-python - ninja - numpy - pip - python - pytest - - setuptools From 2c46c2c5eb1c862c7fd2ca9ed5a74e910b52066a Mon Sep 17 00:00:00 2001 From: Garrett Barter Date: Wed, 17 Jan 2024 11:32:00 -0700 Subject: [PATCH 13/17] try this --- pyframe3dd/pyframe3dd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyframe3dd/pyframe3dd.py b/pyframe3dd/pyframe3dd.py index 36d45af..96265ec 100644 --- a/pyframe3dd/pyframe3dd.py +++ b/pyframe3dd/pyframe3dd.py @@ -33,7 +33,7 @@ os.path.join(os.path.dirname( sysconfig.get_path('stdlib') ), libname), # conda installs os.path.join(os.path.dirname( sysconfig.get_path('stdlib') ), "lib", libname), # conda installs os.path.join(os.path.dirname( sysconfig.get_path('stdlib') ), "Library", "lib", libname), # conda installs - os.path.join( sysconfig.get_path('platlib'), "pyframe3dd", "lib", libname), # system-wide pip installs + os.path.join( sysconfig.get_path('platlib'), "pyframe3dd", libname), # system-wide pip installs os.path.join( sysconfig.get_config_var("userbase"), "lib", "python", "site-packages", "pyframe3dd", libname), # system wide local ] From 3fa93cd52c34760e4ce6fc535d1a1b4482e52267 Mon Sep 17 00:00:00 2001 From: Garrett Barter Date: Wed, 17 Jan 2024 14:25:37 -0700 Subject: [PATCH 14/17] test editable too --- .github/workflows/CI_pyFrame3DD.yml | 25 +++++++++++++++++++++++-- README.md | 15 ++++++++++++++- pyframe3dd/pyframe3dd.py | 1 + 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/.github/workflows/CI_pyFrame3DD.yml b/.github/workflows/CI_pyFrame3DD.yml index bd6aa8f..90b0977 100644 --- a/.github/workflows/CI_pyFrame3DD.yml +++ b/.github/workflows/CI_pyFrame3DD.yml @@ -51,6 +51,18 @@ jobs: - name: Test run run: | '${{ steps.cp.outputs.python-path }}' -m pytest test + + - name: Editable Pip Install pyFrame3DD + env: + CC: '${{ steps.install_cc.outputs.cc }}' + CXX: '${{ steps.install_cc.outputs.cxx }}' + run: | + '${{ steps.cp.outputs.python-path }}' -m pip uninstall pyframe3dd + '${{ steps.cp.outputs.python-path }}' -m pip install -e --no-build-isolation . + + - name: Editable Test run + run: | + '${{ steps.cp.outputs.python-path }}' -m pytest test build_conda: @@ -93,13 +105,11 @@ jobs: run: | conda install -y compilers - # Install - name: Debug run: | conda list printenv - # Install - name: Conda Install pyFrame3DD env: MESON_ARGS: "" @@ -109,4 +119,15 @@ jobs: - name: Test run run: | python -m pytest test + + - name: Editable Conda Install pyFrame3DD + env: + MESON_ARGS: "" + run: | + python -m pip uninstall pyframe3dd + python -m pip install -e --no-build-isolation . -v + + - name: Editable Test run + run: | + python -m pytest test diff --git a/README.md b/README.md index a1bd6fe..93c803e 100644 --- a/README.md +++ b/README.md @@ -16,12 +16,25 @@ There is example code that shows usage contained in ``examples/exB.py``. This f pyFrame3DD requires a C compiler -## Install +## Install (as a library) For detailed installation instructions of WISDEM modules see or to install pyFrame3DD by itself do: $ pip install WISDEM-pyFrame3DD + +## Install (from source) + +If you would like to build the project locally from source for easier access to the underlying methods and tests, do: + + $ git clone https://github.com/WISDEM/pyFrame3DD.git + $ cd pyFrame3DD + $ pip install . + +If developer/editable mode, do the same `git clone` step, but on install do: + + $ pip install -e --no-build-isolation . + ## Unit Tests $ pytest test diff --git a/pyframe3dd/pyframe3dd.py b/pyframe3dd/pyframe3dd.py index 96265ec..cfff4d8 100644 --- a/pyframe3dd/pyframe3dd.py +++ b/pyframe3dd/pyframe3dd.py @@ -31,6 +31,7 @@ lib_opt = [os.path.join(pyframe3dd_dir, libname), # pip installs (regular and editable) os.path.join(os.path.dirname( os.path.dirname( pyframe3dd_dir )), "local", "lib", libname), # WEIS library os.path.join(os.path.dirname( sysconfig.get_path('stdlib') ), libname), # conda installs + os.path.join(os.path.dirname( sysconfig.get_path('stdlib') ), "pyframe3dd", libname), # conda installs os.path.join(os.path.dirname( sysconfig.get_path('stdlib') ), "lib", libname), # conda installs os.path.join(os.path.dirname( sysconfig.get_path('stdlib') ), "Library", "lib", libname), # conda installs os.path.join( sysconfig.get_path('platlib'), "pyframe3dd", libname), # system-wide pip installs From a3558ed23567a0bbb40c276e62857c8156c72f38 Mon Sep 17 00:00:00 2001 From: Garrett Barter Date: Wed, 17 Jan 2024 14:50:12 -0700 Subject: [PATCH 15/17] change order --- .github/workflows/CI_pyFrame3DD.yml | 4 ++-- README.md | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/CI_pyFrame3DD.yml b/.github/workflows/CI_pyFrame3DD.yml index 90b0977..6ffff5d 100644 --- a/.github/workflows/CI_pyFrame3DD.yml +++ b/.github/workflows/CI_pyFrame3DD.yml @@ -58,7 +58,7 @@ jobs: CXX: '${{ steps.install_cc.outputs.cxx }}' run: | '${{ steps.cp.outputs.python-path }}' -m pip uninstall pyframe3dd - '${{ steps.cp.outputs.python-path }}' -m pip install -e --no-build-isolation . + '${{ steps.cp.outputs.python-path }}' -m pip install --no-build-isolation -e . - name: Editable Test run run: | @@ -125,7 +125,7 @@ jobs: MESON_ARGS: "" run: | python -m pip uninstall pyframe3dd - python -m pip install -e --no-build-isolation . -v + python -m pip install --no-build-isolation -e . -v - name: Editable Test run run: | diff --git a/README.md b/README.md index 93c803e..dd0a604 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,10 @@ If you would like to build the project locally from source for easier access to If developer/editable mode, do the same `git clone` step, but on install do: - $ pip install -e --no-build-isolation . + $ pip install --no-build-isolation -e . + +The `--no-build-isolation` option is important per [Meson guidelines](https://meson-python.readthedocs.io/en/latest/how-to-guides/editable-installs.html). + ## Unit Tests From 6c43ad55bcc911fe060006102808c6c689a17b1f Mon Sep 17 00:00:00 2001 From: Garrett Barter Date: Wed, 17 Jan 2024 15:14:33 -0700 Subject: [PATCH 16/17] swapping order of installs --- .github/workflows/CI_pyFrame3DD.yml | 24 ++++++++++++------------ pyframe3dd/pyframe3dd.py | 1 + 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/.github/workflows/CI_pyFrame3DD.yml b/.github/workflows/CI_pyFrame3DD.yml index 6ffff5d..1396a64 100644 --- a/.github/workflows/CI_pyFrame3DD.yml +++ b/.github/workflows/CI_pyFrame3DD.yml @@ -35,11 +35,23 @@ jobs: python-version: ${{ matrix.python-version }} update-environment: true + - name: Editable Pip Install pyFrame3DD + env: + CC: '${{ steps.install_cc.outputs.cc }}' + CXX: '${{ steps.install_cc.outputs.cxx }}' + run: | + '${{ steps.cp.outputs.python-path }}' -m pip install --no-build-isolation -e .[test] + + - name: Editable Test run + run: | + '${{ steps.cp.outputs.python-path }}' -m pytest test + - name: Pip Install pyFrame3DD env: CC: '${{ steps.install_cc.outputs.cc }}' CXX: '${{ steps.install_cc.outputs.cxx }}' run: | + '${{ steps.cp.outputs.python-path }}' -m pip uninstall pyframe3dd '${{ steps.cp.outputs.python-path }}' -m pip install -v .[test] #- name: Setup tmate session @@ -51,18 +63,6 @@ jobs: - name: Test run run: | '${{ steps.cp.outputs.python-path }}' -m pytest test - - - name: Editable Pip Install pyFrame3DD - env: - CC: '${{ steps.install_cc.outputs.cc }}' - CXX: '${{ steps.install_cc.outputs.cxx }}' - run: | - '${{ steps.cp.outputs.python-path }}' -m pip uninstall pyframe3dd - '${{ steps.cp.outputs.python-path }}' -m pip install --no-build-isolation -e . - - - name: Editable Test run - run: | - '${{ steps.cp.outputs.python-path }}' -m pytest test build_conda: diff --git a/pyframe3dd/pyframe3dd.py b/pyframe3dd/pyframe3dd.py index cfff4d8..4605645 100644 --- a/pyframe3dd/pyframe3dd.py +++ b/pyframe3dd/pyframe3dd.py @@ -38,6 +38,7 @@ os.path.join( sysconfig.get_config_var("userbase"), "lib", "python", "site-packages", "pyframe3dd", libname), # system wide local ] +# For Meson's editable installs for p in sys.meta_path: if "_build_path" in p.__dict__: lib_opt += [os.path.join(p._build_path, "pyframe3dd", libname)] From 9b42df3320ca9f0e350a76c04ab6d194e5e67d93 Mon Sep 17 00:00:00 2001 From: Garrett Barter Date: Wed, 17 Jan 2024 15:16:47 -0700 Subject: [PATCH 17/17] try again --- .github/workflows/CI_pyFrame3DD.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/CI_pyFrame3DD.yml b/.github/workflows/CI_pyFrame3DD.yml index 1396a64..9978d6f 100644 --- a/.github/workflows/CI_pyFrame3DD.yml +++ b/.github/workflows/CI_pyFrame3DD.yml @@ -40,6 +40,8 @@ jobs: CC: '${{ steps.install_cc.outputs.cc }}' CXX: '${{ steps.install_cc.outputs.cxx }}' run: | + '${{ steps.cp.outputs.python-path }}' -m pip install --upgrade pip + '${{ steps.cp.outputs.python-path }}' -m pip install meson-python meson numpy ninja wheel '${{ steps.cp.outputs.python-path }}' -m pip install --no-build-isolation -e .[test] - name: Editable Test run