Thanks to visit codestin.com
Credit goes to github.com

Skip to content

BUG: f2py module giving 'symbol not found in flat namespace' on MacOS/Python 3.12 #25266

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

Closed
AlexanderRichert-NOAA opened this issue Nov 28, 2023 · 12 comments

Comments

@AlexanderRichert-NOAA
Copy link

Describe the issue:

On MacOS when using Python 3.12 (i.e., meson rather than distutils), f2py does not appear to be incorporating Fortran-based functions from the provided library. Here's my f2py call: f2py --verbose -c _bufrlib.pyf -m _bufrlib -L${CMAKE_BINARY_DIR}/src -lbufr_4. For whatever reason, unlike under Linux, the objects defined in ${CMAKE_BINARY_DIR}/src/libbufr_4.a are not defined in the output object, i.e., _bufrlib.cpython-312-darwin.so does not contain the functions defined in my .a library. I've had no issues with Python <3.11 when using the distutils backend.

Reproduce the code example:

I'm not sure there's a good way to fit the various relevant source code here, but here's the call: `f2py --verbose -c _bufrlib.pyf -m _bufrlib -L${CMAKE_BINARY_DIR}/src -lbufr_4`. Happy to provide other details/code as needed.

Error message:

On module load:

 Traceback (most recent call last):
  File "/Users/runner/work/NCEPLIBS-bufr/NCEPLIBS-bufr/bufr/python/test/test_checkpoint.py", line 2, in <module>
    import ncepbufr
  File "/Users/runner/work/NCEPLIBS-bufr/NCEPLIBS-bufr/bufr/build/python/ncepbufr/__init__.py", line 1, in <module>
    import _bufrlib
ImportError: dlopen(/Users/runner/work/NCEPLIBS-bufr/NCEPLIBS-bufr/bufr/build/python/_bufrlib.cpython-312-darwin.so, 0x0002): symbol not found in flat namespace (_getbmiss_)


### Runtime information:

1.26.2
3.12.0 (v3.12.0:0fb18b02c8, Oct  2 2023, 09:45:56) [Clang 13.0.0 (clang-1300.0.29.30)]
WARNING: `threadpoolctl` not found in system! Install it by `pip install threadpoolctl`. Once installed, try `np.show_runtime` again for more detailed build information
[{'numpy_version': '1.26.2',
  'python': '3.12.0 (v3.12.0:0fb18b02c8, Oct  2 2023, 09:45:56) [Clang 13.0.0 '
            '(clang-1300.0.29.30)]',
  'uname': uname_result(system='Darwin', node='Mac-1701199105780.local', release='21.6.0', version='Darwin Kernel Version 21.6.0: Thu Jul  6 22:18:26 PDT 2023; root:xnu-8020.240.18.702.13~1/RELEASE_X86_64', machine='x86_64')},
 {'simd_extensions': {'baseline': ['SSE', 'SSE2', 'SSE3'],
                      'found': ['SSSE3',
                                'SSE41',
                                'POPCNT',
                                'SSE42',
                                'AVX',
                                'F16C',
                                'FMA3',
                                'AVX2'],
                      'not_found': ['AVX512F',
                                    'AVX512CD',
                                    'AVX512_KNL',
                                    'AVX512_SKX',
                                    'AVX512_CLX',
                                    'AVX512_CNL',
                                    'AVX512_ICL']}}]
None

### Context for the issue:

The immediate impact of this is that it is blocking MacOS-based CI runs of https://github.com/NOAA-EMC/NCEPLIBS-bufr. The longterm impact is that we will eventually need to be able to support the MacOS+Python 3.12 combination.
@HaoZeke
Copy link
Member

HaoZeke commented Nov 28, 2023

This particular issue is because -L and -l are not supported by the meson backend now, and should be set via environment variables as discussed on the migrating to meson page.

Since the log shows 1.26.2 I think changing the call should be enough. Please reopen if not.

@AlexanderRichert-NOAA
Copy link
Author

Thanks, I'll try it. Is there a clean way to write the call so that it also covers the Python <3.11/distutils case? I guess LDFLAGS="-L${CMAKE_BINARY_DIR}/src" LIBS="-lbufr_4" f2py --verbose -c _bufrlib.pyf -m _bufrlib -L${CMAKE_BINARY_DIR}/src -lbufr_4 might work but it'd be nice to avoid the redundancy if possible.

@HaoZeke
Copy link
Member

HaoZeke commented Nov 28, 2023

Thanks, I'll try it. Is there a clean way to write the call so that it also covers the Python <3.11/distutils case? I guess LDFLAGS="-L${CMAKE_BINARY_DIR}/src" LIBS="-lbufr_4" f2py --verbose -c _bufrlib.pyf -m _bufrlib -L${CMAKE_BINARY_DIR}/src -lbufr_4 might work but it'd be nice to avoid the redundancy if possible.

I would personally drop -L and -l completely and pass --backend meson instead, but even otherwise, I thought distutils also respects environment variables..

If this is a dependency which can be discovered by meson it is worth trying --dep bufr_4 or --dep bufr as well.

@AlexanderRichert-NOAA
Copy link
Author

I can't seem to get it to work either way. In the case of -L/-l (LDFLAGS="-L/path/to/src" LIBS="-lbufr_4" f2py --verbose -c /path/to/_bufrlib.pyf -m _bufrlib --backend meson; also tried putting everything under LIBS) it doesn't seem to have any effect. There's no cmake or pkgconfig info at this step in the build process (this is under a cmake project, and the python module is built on top of the main library, so we haven't run 'make install' yet), so I'm unable to add a bufr target for meson to use.

@HaoZeke
Copy link
Member

HaoZeke commented Nov 28, 2023

Could you try with --build-dir blah and test if:

cd blah
# export LDFLAGS and LIBS here
meson compile -C bbdir

works? Does it work on Linux? (should be the same though)

EDIT: I'll check the upstream source out too.

@AlexanderRichert-NOAA
Copy link
Author

AlexanderRichert-NOAA commented Nov 28, 2023

Thankfully I've been able to reproduce the issue on Linux, which is what my PC uses, and the same thing is happening (so that's good, I guess). I ran with --build-dir etc. and got the same result. I ran meson compile --verbose ... and found that the -L flag was getting incorporated, but not the -l one. I can get around it by putting the -l in LDFLAGS instead of LIBS (or by just setting LDFLAGS to use the path to the library itself).

The problem now is that when I run nm _bufrlib.cpython-312-x86_64-linux-gnu.so, I can see that the various functions are getting populated, but my python module still doesn't load, e.g., AttributeError: module '_bufrlib' has no attribute 'bvers', yet I can see that bvers() is defined in the .so file. For fun, if I delete that reference in my __init__.py, the next problem that pops up is AttributeError: module '_bufrlib' has no attribute 'datelen', and in that case, I can see that there is no datelen() defined in the .so file, even though it is defined both in _bufrlib.pyf and libbufr_4.a.

@HaoZeke
Copy link
Member

HaoZeke commented Nov 28, 2023

Thankfully I've been able to reproduce the issue on Linux, which is what my PC uses, and the same thing is happening (so that's good, I guess). I ran with --build-dir etc. and got the same result. I ran meson compile --verbose ... and found that the -L flag was getting incorporated, but not the -l one. I can get around it by putting the -l in LDFLAGS instead of LIBS (or by just setting LDFLAGS to use the path to the library itself).

The problem now is that when I run nm _bufrlib.cpython-312-x86_64-linux-gnu.so, I can see that the various functions are getting populated, but my python module still doesn't load, e.g., AttributeError: module '_bufrlib' has no attribute 'bvers', yet I can see that bvers() is defined in the .so file. For fun, if I delete that reference in my init.py, the next problem that pops up is AttributeError: module '_bufrlib' has no attribute 'datelen', and in that case, I can see that there is no datelen() defined in the .so file, even though it is defined both in _bufrlib.pyf and libbufr_4.a.

Could you add steps to reproduce this? I'm reopening this since it seems to be an f2py build thing..

@HaoZeke HaoZeke reopened this Nov 28, 2023
@AlexanderRichert-NOAA
Copy link
Author

Here's the command my cmake/make is running:

/usr/bin/cmake -E env LDFLAGS=-L/n/NCEPLIBS_CI_Updates_Nov2023/NCEPLIBS-bufr/build/src\ -lbufr_4 /opt/spack_nov23/var/spack/environments/python-312-gcc-11/install/linux-centos8-zen3/gcc-11.2.1/python-3.12.0-khbplgh6i6jpf7i6znui44zdubchaw23/bin/f2py --verbose -c /n/NCEPLIBS_CI_Updates_Nov2023/NCEPLIBS-bufr/python/_bufrlib.pyf -m _bufrlib --backend meson

Let me know if there's more I can provide; it might take a while to distill it down to a simplified test case but I can do that if needed. As far as what I'm running:

# (python 3.12 and meson 1.2.2 installed through spack; numpy 1.26.2 installed through pip)
git clone https://github.com/AlexanderRichert-NOAA/NCEPLIBS-bufr -b ci_updates_nov2023
cd NCEPLIBS-bufr/
mkdir build && cd build/
cmake .. -DENABLE_PYTHON=ON -DBUILD_TESTING=OFF -DCMAKE_INSTALL_PREFIX=$PWD/install
make -j2
export PYTHONPATH=$(realpath python):$PYTHONPATH
python -c "import ncepbufr"

Result:

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/dev/shm/NCEPLIBS-bufr/build/python/ncepbufr/__init__.py", line 9, in <module>
    __bufrlib_version__ = _bufrlib.bvers().rstrip()
                          ^^^^^^^^^^^^^^
AttributeError: module '_bufrlib' has no attribute 'bvers'

@HaoZeke
Copy link
Member

HaoZeke commented Nov 28, 2023

Let me know if there's more I can provide; it might take a while to distill it down to a simplified test case but I can do that if needed. As far as what I'm running:

That'd be awesome if you could, I'll likely not be able to dig deep into this until the weekend otherwise.

@AlexanderRichert-NOAA
Copy link
Author

I'm attaching a zip file with a test setup (just run . sourceme.sh), or if you're not feeling adventurous enough to download a zip file, here are the contents:

sourceme.sh:

# Build static archive:
gcc -fPIC -c bvers.f -o libbvers.a

### Python 3.6/distutils backend (works for me):
python3.6 -m numpy.f2py --verbose -c _bufrlib.pyf -m _bufrlib -L$PWD -lbvers --build-dir builddir-distutils
python3.6 -c "import ncepbufr" |& tee python36.output

### Python 3.12/Meson backend (does not work for me):
. /opt/spack_nov23/share/spack/setup-env.sh
spack env activate python-312-gcc-11
spack load python meson

rm _bufrlib.cpython-312*.so
LDFLAGS=$PWD/libbvers.a python3.12 -m numpy.f2py --verbose -c _bufrlib.pyf -m _bufrlib --backend meson --build-dir builddir-mesonA
python3.12 -c "import ncepbufr" |& tee python312_A.output
rm _bufrlib.cpython-312*.so
LDFLAGS="-L$PWD -lbvers" python3.12 -m numpy.f2py --verbose -c _bufrlib.pyf -m _bufrlib --backend meson --build-dir builddir-mesonB
python3.12 -c "import ncepbufr" |& tee python312_B.output
rm _bufrlib.cpython-312*.so
CFLAGS="-L$PWD -lbvers" python3.12 -m numpy.f2py --verbose -c _bufrlib.pyf -m _bufrlib --backend meson --build-dir builddir-mesonC
python3.12 -c "import ncepbufr" |& tee python312_C.output

bvers.f:

        SUBROUTINE BVERS (CVERSTR)
        CHARACTER*(*)   CVERSTR
        CVERSTR = 'myver'
        RETURN
        END

_bufrlib.pyf:

python module _bufrlib
    interface
        subroutine bvers(cverstr) ! in bvers.f
            character*12,intent(out) :: cverstr
        end subroutine bvers
    end interface 
end python module _bufrlib

ncepbufr/__init__.py:

import _bufrlib
__bufrlib_version__ = _bufrlib.bvers().rstrip()
print("successful load")

f2pytestcase_28nov23.zip

@HaoZeke
Copy link
Member

HaoZeke commented Dec 18, 2023

I can't seem to reproduce this locally anymore:

❯ tree .
.
β”œβ”€β”€ _bufrlib.pyf
β”œβ”€β”€ bvers.f
β”œβ”€β”€ libbvers.a
β”œβ”€β”€ ncepbufr
└── sourceme.sh

2 directories, 4 files

# Compile
❯ gcc -fPIC -c bvers.f -o libbvers.a
# Version check (main)
❯ f2py -v
2.0.0.dev0+git20231209.35c4319

# Distutils
❯ f2py --verbose -c _bufrlib.pyf -m _bufrlib -L$PWD -lbvers --build-dir builddir-distutils
❯ python -c "import ncepbufr" |& tee distutils.output
successful load

# Cleanup
❯ rm -rf *.output *.so builddir*

# Meson
❯ f2py --verbose -c _bufrlib.pyf -m _bufrlib -L$PWD -lbvers --backend meson --build-dir builddir-meson
❯ python -c "import ncepbufr" |& tee meson.output
successful load

The meson.build should include:

bvers = declare_dependency(link_args : ['-lbvers'])
lib_dir_0 = declare_dependency(link_args : ['-L/home/rgoswami/Git/Github/Quansight/numscipy_playground/numpy_bugs/gh-25266'])

py.extension_module('_bufrlib',
                     [
'_bufrlibmodule.c',
                     '_bufrlib-f2pywrappers.f',
                     fortranobject_c
                     ],
                     include_directories: [inc_np],
                     dependencies : [
                     py_dep,
                     quadmath_dep,

bvers,
lib_dir_0,
                     ],
                     install : true)

Which was added last week in #25297. Please reopen if it persists @AlexanderRichert-NOAA.

@HaoZeke HaoZeke closed this as completed Dec 18, 2023
@AlexanderRichert-NOAA
Copy link
Author

Thanks @HaoZeke. I'm unable to test at the moment, but #25297 certainly looks directly relevant. Do you know when that will be in a release?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants