test_compile_function.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. """See https://github.com/numpy/numpy/pull/11937.
  2. """
  3. import sys
  4. import os
  5. import uuid
  6. from importlib import import_module
  7. import pytest
  8. import numpy.f2py
  9. from . import util
  10. def setup_module():
  11. if not util.has_c_compiler():
  12. pytest.skip("Needs C compiler")
  13. if not util.has_f77_compiler():
  14. pytest.skip("Needs FORTRAN 77 compiler")
  15. # extra_args can be a list (since gh-11937) or string.
  16. # also test absence of extra_args
  17. @pytest.mark.parametrize("extra_args",
  18. [["--noopt", "--debug"], "--noopt --debug", ""])
  19. @pytest.mark.leaks_references(reason="Imported module seems never deleted.")
  20. def test_f2py_init_compile(extra_args):
  21. # flush through the f2py __init__ compile() function code path as a
  22. # crude test for input handling following migration from
  23. # exec_command() to subprocess.check_output() in gh-11937
  24. # the Fortran 77 syntax requires 6 spaces before any commands, but
  25. # more space may be added/
  26. fsource = """
  27. integer function foo()
  28. foo = 10 + 5
  29. return
  30. end
  31. """
  32. # use various helper functions in util.py to enable robust build /
  33. # compile and reimport cycle in test suite
  34. moddir = util.get_module_dir()
  35. modname = util.get_temp_module_name()
  36. cwd = os.getcwd()
  37. target = os.path.join(moddir, str(uuid.uuid4()) + ".f")
  38. # try running compile() with and without a source_fn provided so
  39. # that the code path where a temporary file for writing Fortran
  40. # source is created is also explored
  41. for source_fn in [target, None]:
  42. # mimic the path changing behavior used by build_module() in
  43. # util.py, but don't actually use build_module() because it has
  44. # its own invocation of subprocess that circumvents the
  45. # f2py.compile code block under test
  46. with util.switchdir(moddir):
  47. ret_val = numpy.f2py.compile(fsource,
  48. modulename=modname,
  49. extra_args=extra_args,
  50. source_fn=source_fn)
  51. # check for compile success return value
  52. assert ret_val == 0
  53. # we are not currently able to import the Python-Fortran
  54. # interface module on Windows / Appveyor, even though we do get
  55. # successful compilation on that platform with Python 3.x
  56. if sys.platform != "win32":
  57. # check for sensible result of Fortran function; that means
  58. # we can import the module name in Python and retrieve the
  59. # result of the sum operation
  60. return_check = import_module(modname)
  61. calc_result = return_check.foo()
  62. assert calc_result == 15
  63. # Removal from sys.modules, is not as such necessary. Even with
  64. # removal, the module (dict) stays alive.
  65. del sys.modules[modname]
  66. def test_f2py_init_compile_failure():
  67. # verify an appropriate integer status value returned by
  68. # f2py.compile() when invalid Fortran is provided
  69. ret_val = numpy.f2py.compile(b"invalid")
  70. assert ret_val == 1
  71. def test_f2py_init_compile_bad_cmd():
  72. # verify that usage of invalid command in f2py.compile() returns
  73. # status value of 127 for historic consistency with exec_command()
  74. # error handling
  75. # patch the sys Python exe path temporarily to induce an OSError
  76. # downstream NOTE: how bad of an idea is this patching?
  77. try:
  78. temp = sys.executable
  79. sys.executable = "does not exist"
  80. # the OSError should take precedence over invalid Fortran
  81. ret_val = numpy.f2py.compile(b"invalid")
  82. assert ret_val == 127
  83. finally:
  84. sys.executable = temp
  85. @pytest.mark.parametrize(
  86. "fsource",
  87. [
  88. "program test_f2py\nend program test_f2py",
  89. b"program test_f2py\nend program test_f2py",
  90. ],
  91. )
  92. def test_compile_from_strings(tmpdir, fsource):
  93. # Make sure we can compile str and bytes gh-12796
  94. with util.switchdir(tmpdir):
  95. ret_val = numpy.f2py.compile(fsource,
  96. modulename="test_compile_from_strings",
  97. extension=".f90")
  98. assert ret_val == 0