8000 TST: Use ``meson`` for testing ``f2py`` by HaoZeke · Pull Request #25111 · numpy/numpy · GitHub
[go: up one dir, main page]

Skip to content

TST: Use meson for testing f2py #25111

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 26 commits into from
Nov 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
8d04cbf
MAINT,TST: Use meson for compiler checks [f2py]
HaoZeke Nov 11, 2023
5a2fa25
MAINT: Cache the compiler checks [f2py]
HaoZeke Nov 11, 2023
1fdc2f8
MAINT,TST: Always use meson [f2py]
HaoZeke Nov 11, 2023
babda1d
TST: Rework to have a build_meson [f2py]
HaoZeke Nov 11, 2023
278890e
MAINT,TST: Simplify the meson backend [f2py]
HaoZeke Nov 11, 2023
2f1b5c8
TST: Use the build_meson function [f2py]
HaoZeke Nov 11, 2023
9f75919
MAINT,TST: Minor cleanup [f2py]
HaoZeke Nov 11, 2023
73edb6f
TST: Ensure TestDocAdvanced runs with spin [f2py]
HaoZeke Nov 11, 2023
51a760a
TST: Use cleanup meson backend [f2py]
HaoZeke Nov 12, 2023
4da19cf
MAINT,TST: Generalize build_meson [f2py]
HaoZeke Nov 12, 2023
6122eb7
TST: Use a helper for spin tests [f2py]
HaoZeke Nov 12, 2023
d678b61
MAINT: Simplify meson backend [f2py]
HaoZeke Nov 12, 2023
0397ee4
TST: Handle unsupported compilers [f2py]
HaoZeke Nov 12, 2023
74934a5
TST: Fix gibberish in [f2py] documentation test
HaoZeke Nov 12, 2023
4154877
CI: Add meson for cygwin runs
HaoZeke Nov 12, 2023
fe5bf64
TST: Skips for 32-bit errors [f2py]
HaoZeke Nov 12, 2023
6cdecd0
TST: Skip for cygwin since meson is old [f2py]
HaoZeke Nov 12, 2023
dd6f221
CI: Revert grabbing meson on cygwin
HaoZeke Nov 12, 2023
ef17ab5
TST: Cleanup old distutils builder [f2py]
HaoZeke Nov 12, 2023
8d7ec08
TST: Skip cygwin better [f2py]
HaoZeke Nov 12, 2023
b0f418e
TST: Don't touch distutils
HaoZeke Nov 19, 2023
5bce3b4
MAINT: Vendor in distutils testing requirement
HaoZeke Nov 19, 2023
9fc2006
TST: Try removing cygwin restrictions
HaoZeke Nov 21, 2023
2ac2902
MAINT: Cleanup some tests [f2py]
HaoZeke Nov 23, 2023
63cbffb
TST: Try to use concurrency for i/o bounds [f2py]
HaoZeke Nov 23, 2023
64f4fd2
TST: Mark slow tests [f2py]
HaoZeke Nov 23, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion numpy/distutils/tests/test_build_ext.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def test_multi_fortran_libs_link(tmp_path):
# We need to make sure we actually have an f77 compiler.
# This is nontrivial, so we'll borrow the utilities
# from f2py tests:
from numpy.f2py.tests.util import has_f77_compiler
from numpy.distutils.tests.utilities import has_f77_compiler
if not has_f77_compiler():
pytest.skip('No F77 compiler found')

Expand Down
90 changes: 90 additions & 0 deletions numpy/distutils/tests/utilities.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# Kanged out of numpy.f2py.tests.util for test_build_ext
from numpy.testing import IS_WASM
import textwrap
import shutil
import tempfile
import os
import re
import subprocess
import sys

#
# Check if compilers are available at all...
#

_compiler_status = None


def _get_compiler_status():
global _compiler_status
if _compiler_status is not None:
return _compiler_status

_compiler_status = (False, False, False)
if IS_WASM:
# Can't run compiler from inside WASM.
return _compiler_status

# XXX: this is really ugly. But I don't know how to invoke Distutils
# in a safer way...
code = textwrap.dedent(
f"""\
import os
import sys
sys.path = {repr(sys.path)}

def configuration(parent_name='',top_path=None):
global config
from numpy.distutils.misc_util import Configuration
config = Configuration('', parent_name, top_path)
return config

from numpy.distutils.core import setup
setup(configuration=configuration)

config_cmd = config.get_config_cmd()
have_c = config_cmd.try_compile('void foo() {{}}')
print('COMPILERS:%%d,%%d,%%d' %% (have_c,
config.have_f77c(),
config.have_f90c()))
sys.exit(99)
"""
)
code = code % dict(syspath=repr(sys.path))

tmpdir = tempfile.mkdtemp()
try:
script = os.path.join(tmpdir, "setup.py")

with open(script, "w") as f:
f.write(code)

cmd = [sys.executable, "setup.py", "config"]
p = subprocess.Popen(
cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=tmpdir
)
out, err = p.communicate()
finally:
shutil.rmtree(tmpdir)

m = re.search(rb"COMPILERS:(\d+),(\d+),(\d+)", out)
if m:
_compiler_status = (
bool(int(m.group(1))),
bool(int(m.group(2))),
bool(int(m.group(3))),
)
# Finished
return _compiler_status


def has_c_compiler():
return _get_compiler_status()[0]


def has_f77_compiler():
return _get_compiler_status()[1]


def has_f90_compiler():
return _get_compiler_status()[2]
26 changes: 7 additions & 19 deletions numpy/f2py/_backends/_meson.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,6 @@ def _move_exec_to_root(self, build_dir: Path):
for path_object in path_objects:
shutil.move(path_object, Path.cwd())

def _get_build_command(self):
return [
"meson",
"setup",
self.meson_build_dir,
]

def write_meson_build(self, build_dir: Path) -> None:
"""Writes the meson build file at specified location"""
meson_template = MesonTemplate(
Expand All @@ -115,19 +108,14 @@ def write_meson_build(self, build_dir: Path) -> None:
meson_build_file.write_text(src)
return meson_build_file

def _run_subprocess_command(self, command, cwd):
subprocess.run(command, cwd=cwd, check=True)

def run_meson(self, build_dir: Path):
completed_process = subprocess.run(self._get_build_command(), cwd=build_dir)
if completed_process.returncode != 0:
raise subprocess.CalledProcessError(
completed_process.returncode, completed_process.args
)
completed_process = subprocess.run(
["meson", "compile", "-C", self.meson_build_dir], cwd=build_dir
)
if completed_process.returncode != 0:
raise subprocess.CalledProcessError(
completed_process.returncode, completed_process.args
)
setup_command = ["meson", "setup", self.meson_build_dir]
self._run_subprocess_command(setup_command, build_dir)
compile_command = ["meson", "compile", "-C", self.meson_build_dir]
self._run_subprocess_command(compile_command, build_dir)

def compile(self) -> None:
self.sources = _prepare_sources(self.modulename, self.sources, self.build_dir)
Expand Down
1 change: 1 addition & 0 deletions numpy/f2py/tests/test_abstract_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@


@pytest.mark.skipif(IS_WASM, reason="Cannot start subprocess")
@pytest.mark.slow
class TestAbstractInterface(util.F2PyTest):
sources = [util.getpath("tests", "src", "abstract_interface", "foo.f90")]

Expand Down
22 changes: 7 additions & 15 deletions numpy/f2py/tests/test_array_from_pyobj.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import copy
import platform
import pytest
from pathlib import Path

import numpy as np

Expand All @@ -19,31 +20,22 @@
)


def get_testdir():
testroot = Path(__file__).resolve().parent / "src"
return testroot / "array_from_pyobj"

def setup_module():
"""
Build the required testing extension module

"""
global wrap

# Check compiler availability first
if not util.has_c_compiler():
pytest.skip("No C compiler available")

if wrap is None:
config_code = """
config.add_extension('test_array_from_pyobj_ext',
sources=['wrapmodule.c', 'fortranobject.c'],
define_macros=[])
"""
d = os.path.dirname(__file__)
src = [
util.getpath("tests", "src", "array_from_pyobj", "wrapmodule.c"),
util.getpath("src", "fortranobject.c"),
util.getpath("src", "fortranobject.h"),
get_testdir() / "wrapmodule.c",
]
wrap = util.build_module_distutils(src, config_code,
"test_array_from_pyobj_ext")
wrap = util.build_meson(src, module_name = "test_array_from_pyobj_ext")


def flags_info(arr):
Expand Down
1 change: 1 addition & 0 deletions numpy/f2py/tests/test_block_docstring.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from numpy.testing import IS_PYPY


@pytest.mark.slow
class TestBlockDocString(util.F2PyTest):
sources = [util.getpath("tests", "src", "block_docstring", "foo.f")]

Expand Down
5 changes: 4 additions & 1 deletion numpy/f2py/tests/test_callback.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class TestF77Callback(util.F2PyTest):
sources = [util.getpath("tests", "src", "callback", "foo.f")]

@pytest.mark.parametrize("name", "t,t2".split(","))
@pytest.mark.slow
def test_all(self, name):
self.check_function(name)

Expand Down Expand Up @@ -205,6 +206,7 @@ class TestF77CallbackPythonTLS(TestF77Callback):
class TestF90Callback(util.F2PyTest):
sources = [util.getpath("tests", "src", "callback", "gh17797.f90")]

@pytest.mark.slow
def test_gh17797(self):
def incr(x):
return x + 123
Expand All @@ -222,6 +224,7 @@ class TestGH18335(util.F2PyTest):
"""
sources = [util.getpath("tests", "src", "callback", "gh18335.f90")]

@pytest.mark.slow
def test_gh18335(self):
def foo(x):
x[0] += 1
Expand All @@ -235,7 +238,7 @@ class TestGH25211(util.F2PyTest):
util.getpath("tests", "src", "callback", "gh25211.pyf")]
module_name = "callback2"

def test_gh18335(self):
def test_gh25211(self):
def bar(x):
return x*x

Expand Down
3 changes: 3 additions & 0 deletions numpy/f2py/tests/test_character.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from numpy.f2py.tests import util


@pytest.mark.slow
class TestCharacterString(util.F2PyTest):
# options = ['--debug-capi', '--build-dir', '/tmp/test-build-f2py']
suffix = '.f90'
Expand Down Expand Up @@ -512,6 +513,7 @@ class TestMiscCharacter(util.F2PyTest):
end subroutine {fprefix}_character_bc_old
""")

@pytest.mark.slow
def test_gh18684(self):
# Test character(len=5) and character*5 usages
f = getattr(self.module, self.fprefix + '_gh18684')
Expand Down Expand Up @@ -596,6 +598,7 @@ class TestStringAssumedLength(util.F2PyTest):
def test_gh24008(self):
self.module.greet("joe", "bob")

@pytest.mark.slow
class TestStringOptionalInOut(util.F2PyTest):
sources = [util.getpath("tests", "src", "string", "gh24662.f90")]

Expand Down
9 changes: 1 addition & 8 deletions numpy/f2py/tests/test_common.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
import os
import sys
import pytest

import numpy as np
from . import util


@pytest.mark.slow
class TestCommonBlock(util.F2PyTest):
sources = [util.getpath("tests", "src", "common", "block.f")]

@pytest.mark.skipif(sys.platform == "win32",
reason="Fails with MinGW64 Gfortran (Issue #9673)")
def test_common_block(self):
self.module.initcb()
assert self.module.block.long_bn == np.array(1.0, dtype=np. F987 float64)
Expand All @@ -21,7 +16,5 @@ def test_common_block(self):
class TestCommonWithUse(util.F2PyTest):
sources = [util.getpath("tests", "src", "common", "gh19161.f90")]

@pytest.mark.skipif(sys.platform == "win32",
reason="Fails with MinGW64 Gfortran (Issue #9673)")
def test_common_gh19161(self):
assert self.module.data.x == 0
3 changes: 3 additions & 0 deletions numpy/f2py/tests/test_crackfortran.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ class TestDimSpec(util.F2PyTest):
)

@pytest.mark.parametrize("dimspec", all_dimspecs)
@pytest.mark.slow
def test_array_size(self, dimspec):

count = self.all_dimspecs.index(dimspec)
Expand Down Expand Up @@ -276,6 +277,7 @@ def test_input_encoding(self, tmp_path, encoding):
assert mod[0]['name'] == 'foo'


@pytest.mark.slow
class TestUnicodeComment(util.F2PyTest):
sources = [util.getpath("tests", "src", "crackfortran", "unicode_comment.f90")]

Expand Down Expand Up @@ -327,6 +329,7 @@ def test_nameargspattern_backtracking(self, adversary):
class TestFunctionReturn(util.F2PyTest):
sources = [util.getpath("tests", "src", "crackfortran", "gh23598.f90")]

@pytest.mark.slow
def test_function_rettype(self):
# gh-23598
assert self.module.intproduct(3, 4) == 12
Expand Down
1 change: 1 addition & 0 deletions numpy/f2py/tests/test_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class TestData(util.F2PyTest):
sources = [util.getpath("tests", "src", "crackfortran", "data_stmts.f90")]

# For gh-23276
@pytest.mark.slow
def test_data_stmts(self):
assert self.module.cmplxdat.i == 2
assert self.module.cmplxdat.j == 3
Expand Down
28 changes: 12 additions & 16 deletions numpy/f2py/tests/test_docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,23 @@
import numpy as np
from numpy.testing import assert_array_equal, assert_equal
from . import util

from pathlib import Path

def get_docdir():
# assuming that documentation tests are run from a source
# directory
return os.path.abspath(os.path.join(
os.path.dirname(__file__),
'..', '..', '..',
'doc', 'source', 'f2py', 'code'))

# Assumes that spin is used to run tests
nproot = Path(__file__).resolve().parents[8]
return nproot / "doc" / "source" / "f2py" / "code"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this required for this PR?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For F2PY, yes, these tests are currently skipped, but for meson and F2PY, not really..


pytestmark = pytest.mark.skipif(
not os.path.isdir(get_docdir()),
reason=('Could not find f2py documentation sources'
f' ({get_docdir()} does not exists)'))


def _path(*a):
return os.path.join(*((get_docdir(),) + a))
not get_docdir().is_dir(),
reason=f"Could not find f2py documentation sources"
f"({get_docdir()} does not exist)",
)

def _path(*args):
return get_docdir().joinpath(*args)

@pytest.mark.slow
class TestDocAdvanced(util.F2PyTest):
# options = ['--debug-capi', '--build-dir', '/tmp/build-f2py']
sources = [_path('asterisk1.f90'), _path('asterisk2.f90'),
Expand All @@ -37,7 +33,7 @@ def test_asterisk2(self):
foo = getattr(self.module, 'foo2')
assert_equal(foo(2), b'12')
assert_equal(foo(12), b'123456789A12')
assert_equal(foo(24), b'123456789A123456789B')
assert_equal(foo(20), b'123456789A123456789B')

< FB5D /td>
def test_ftype(self):
ftype = self.module
Expand Down
2 changes: 1 addition & 1 deletion numpy/f2py/tests/test_f2cmap.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class TestF2Cmap(util.F2PyTest):
]

# gh-15095
def test_long_long_map(self):
def test_gh15095(self):
inp = np.ones(3)
out = self.module.func1(inp)
exp_out = 3
Expand Down
2 changes: 2 additions & 0 deletions numpy/f2py/tests/test_isoc.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
from . import util
import numpy as np
import pytest

class TestISOC(util.F2PyTest):
sources = [
util.getpath("tests", "src", "isocintrin", "isoCtests.f90"),
]

# gh-24553
@pytest.mark.slow
def test_c_double(self):
out = self.module.coddity.c_add(1, 2)
exp_out = 3
Expand Down
Loading
0