Repository: vispy/vispy

Issue:
Unable to import on macOS Big Sur
From https://github.com/vispy/vispy/issues/1484#issuecomment-651875603:

> [`vispy/ext/cocoapy.py`](https://github.com/vispy/vispy/blob/master/vispy/ext/cocoapy.py) calls `cdll.LoadLibrary(util.find_library('...'))` a bunch of times, but ctypes can't find these libraries on Big Sur. On Catalina, `util.find_library('objc')` returns `/usr/lib/libobjc.dylib` and on Big Sur, `util.find_library('objc')` returns `None`. These are all the libraries loaded in `cocoapy.py`:
> - objc
> - CoreFoundation
> - AppKit
> - quartz
> - CoreText
> - Foundation
> 
> https://docs.python.org/3/library/ctypes.html#finding-shared-libraries
> https://docs.python.org/3/library/ctypes.html#ctypes.LibraryLoader.LoadLibrary


## Reproduction
1. `python3 -m pip install vispy`
2. `python3 -c "import vispy"`; the output is:
```
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/private/tmp/venv/lib/python3.8/site-packages/vispy/__init__.py", line 30, in <module>
    from .util import config, set_log_level, keys, sys_info  # noqa
  File "/private/tmp/venv/lib/python3.8/site-packages/vispy/util/__init__.py", line 14, in <module>
    from . import fonts       # noqa
  File "/private/tmp/venv/lib/python3.8/site-packages/vispy/util/fonts/__init__.py", line 13, in <module>
    from ._triage import _load_glyph, list_fonts  # noqa, analysis:ignore
  File "/private/tmp/venv/lib/python3.8/site-packages/vispy/util/fonts/_triage.py", line 14, in <module>
    from ._quartz import _load_glyph, _list_fonts
  File "/private/tmp/venv/lib/python3.8/site-packages/vispy/util/fonts/_quartz.py", line 12, in <module>
    from ...ext.cocoapy import cf, ct, quartz, CFRange, CFSTR, CGGlyph, UniChar, \
  File "/private/tmp/venv/lib/python3.8/site-packages/vispy/ext/cocoapy.py", line 1126, in <module>
    NSEventTrackingRunLoopMode = c_void_p.in_dll(
ValueError: dlsym(RTLD_DEFAULT, NSEventTrackingRunLoopMode): symbol not found
```

See https://github.com/napari/napari/issues/1393 and https://github.com/vispy/vispy/issues/1484. 


Discussion:
According to https://developer.apple.com/documentation/macos-release-notes/macos-big-sur-11-beta-release-notes:

> New in macOS Big Sur 11 beta, the system ships with a built-in dynamic linker cache of all system-provided libraries. As part of this change, copies of dynamic libraries are no longer present on the filesystem. Code that attempts to check for dynamic library presence by looking for a file at a path or enumerating a directory will fail. Instead, check for library presence by attempting to `dlopen()` the path, which will correctly check for the library in the cache. (62986286)

Others have encountered this problem:
- https://bugs.python.org/issue41116 (this is a build issue, but is related I think)
- https://stackoverflow.com/questions/62587131/macos-big-sur-python-ctypes-find-library-does-not-find-libraries-ssl-corefou
- https://www.reddit.com/r/MacOSBeta/comments/hfknpa/is_corefoundation_missing_for_everyone_on_big_sur/
- https://github.com/espressif/esptool/issues/540
---

It should be possible to patch [the relevant code](https://github.com/python/cpython/blob/1648c99932f39f1c60972bb114e6a7bd65523818/Lib/ctypes/util.py#L70-L81) in Python itself. 

Pros:
- vispy and other libraries won't need to change anything

Cons:
- changes will only be reflected in the next Python version

Wow nice job tracking this down. I assume this is big enough that a patch in python might be created and released as a bug release. So then we might get something working in a 3.7.x and 3.8.x release. I guess we'll keep this open for a couple more months and see what has happened by then.

If you have any other updates on progress on this feel free to post it here.
https://github.com/python/cpython/blob/master/Misc/NEWS.d/next/macOS/2020-06-24-13-51-57.bpo-41100.mcHdc5.rst says:
> Big Sur is expected to be fully supported in a future bugfix release of Python 3.8.x and with 3.9.0.

So I'm not sure if Big Sur fixes will be pushed to 3.7 ://
Any ideas @sumanthratna if this has been fixed in newer versions of python? I just released vispy 0.6.5 which includes a Python 3.8 wheel so I'm hoping this works now.
Hi @djhoese! This hasn't been fixed yet; here are the PRs: https://github.com/python/cpython/pull/21249, https://github.com/python/cpython/pull/21576, https://github.com/python/cpython/pull/21577, https://github.com/python/cpython/pull/21241, https://github.com/python/cpython/pull/21242, https://github.com/python/cpython/pull/21239

I pulled vispy 0.6.5 anyway and the `ValueError: dlsym(RTLD_DEFAULT, NSEventTrackingRunLoopMode): symbol not found` error still occurs
While waiting for a fix you can replace https://github.com/vispy/vispy/blob/7f3f6f60c8462bb8a3a8fa03344a2e6990b86eb2/vispy/ext/cocoapy.py#L1123
with
```python
appkit = cdll.LoadLibrary('/System/Library/Frameworks/AppKit.framework/Versions/Current/AppKit')
```

By the way, looks like the upstream cpython patch worked, and python 3.9.1_1 doesn't have this error (I can successfully install and import vispy on Big Sur). 

I don't think this has been resolved in Python 3.8 (the patch is being backported right now) so I'm leaving this issue open until 3.8 is fixed. 

@djhoese if you feel like Python 3.8 + Big Sur support is urgent for vispy, you could check the OS and python version and hard-code the paths to libraries (although I would probably just wait until the backport is finished)

For the record, here's the output of `ctypes.util.find_library` on Python 3.9 and Big Sur:
- `ctypes.util.find_library('AppKit')` -> `/System/Library/Frameworks/AppKit.framework/AppKit`
- `ctypes.util.find_library('objc')` -> `/usr/lib/libobjc.dylib`
@sumanthratna Thanks for all the info and @0x0L for the the example. Given the limited time I have right now and the small-ish user group that this effects, I think I'll wait for the Python 3.8 backport unless a large group of users requires it. I think hacking your own installation of vispy with the above paths is the simplest solution right now that doesn't require a short lived builtin workaround in vispy. If people disagree with this and think a workaround should be added to vispy and it is urgent then let me know and make a pull request with the fix. I'm ok leaving this open until the 3.8 backport is available. @sumanthratna If you are keeping up to date on this, please update this issue when the backport is available. Otherwise, thanks for your help so far.
@djhoese Actually BigSur default /usr/bin/python3 looks ok. The easiest way may be to just copy over `/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/ctypes/macholib/dyld.py` into your python ctypes

I'm running into this issue with the latest python 3.9.1 build from conda-forge (h1d169a7_2_cpython) - anyone know why that might be?
I'm running into this issue with the latest python 3.9.1 build also

`python                    3.9.1           h1d169a7_3_cpython    conda-forge`
If someone can track down why the above mention patch on CPython isn't showing up in the version distributed by conda-forge it would be very much appreciated (download the source that conda-forge is building and find the exact patch for this)?

If this isn't actually fixed then I guess we can add a workaround, but obviously it is unfortunate that we have to do it.
Hi @djhoese I tried the patch to no avail :-(, it also looks like it does contain the patch.

```python

elif os.name == "posix" and sys.platform == "darwin":
    from ctypes.macholib.dyld import dyld_find as _dyld_find
    def find_library(name):
        possible = ['@executable_path/../lib/lib%s.dylib' % name,
                    'lib%s.dylib' % name,
                    '%s.dylib' % name,
                    '%s.framework/%s' % (name, name)]
        for name in possible:
            try:
                return _dyld_find(name)
            except ValueError:
                continue
        return None
```
@goanpeca So with your above patch to the vispy source code you still see an issue? Are you seeing the exact same error as the original post?

@sumanthratna Is it possible when you got this working you were using a patched version of vispy?

@goanpeca What do you get on your system when you run the `find_library` calls that @sumanthratna mentioned in this comment:

https://github.com/vispy/vispy/issues/1885#issuecomment-749164594

```

    ctypes.util.find_library('AppKit') -> /System/Library/Frameworks/AppKit.framework/AppKit
    ctypes.util.find_library('objc') -> /usr/lib/libobjc.dylib

```
Hi @djhoese, running `import vispy` yields:  (I am actually trying to run `napari`)

```python
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/goanpeca/miniconda3/envs/napari/lib/python3.9/site-packages/vispy/__init__.py", line 30, in <module>
    from .util import config, set_log_level, keys, sys_info  # noqa
  File "/Users/goanpeca/miniconda3/envs/napari/lib/python3.9/site-packages/vispy/util/__init__.py", line 14, in <module>
    from . import fonts       # noqa
  File "/Users/goanpeca/miniconda3/envs/napari/lib/python3.9/site-packages/vispy/util/fonts/__init__.py", line 13, in <module>
    from ._triage import _load_glyph, list_fonts  # noqa, analysis:ignore
  File "/Users/goanpeca/miniconda3/envs/napari/lib/python3.9/site-packages/vispy/util/fonts/_triage.py", line 14, in <module>
    from ._quartz import _load_glyph, _list_fonts
  File "/Users/goanpeca/miniconda3/envs/napari/lib/python3.9/site-packages/vispy/util/fonts/_quartz.py", line 12, in <module>
    from ...ext.cocoapy import cf, ct, quartz, CFRange, CFSTR, CGGlyph, UniChar, \
  File "/Users/goanpeca/miniconda3/envs/napari/lib/python3.9/site-packages/vispy/ext/cocoapy.py", line 1126, in <module>
    NSEventTrackingRunLoopMode = c_void_p.in_dll(
ValueError: dlsym(RTLD_DEFAULT, NSEventTrackingRunLoopMode): symbol not found
```
@djhoese they both return `None`, if I manually do this patch, it does work, so it seems the patch needs patching ?

`appkit = cdll.LoadLibrary('/System/Library/Frameworks/AppKit.framework/Versions/Current/AppKit')`

> @sumanthratna Is it possible when you got this working you were using a patched version of vispy?

I just retried on a fresh install (new venv) of vispy and it works the same as before (fails on 3.8.7, works on 3.9.1). 

I am using conda and conda-forge 🙃 ...
@sumanthratna Where/how did you install Python 3.9? If not using conda with conda-forge, would you mind checking it?
> @sumanthratna Where/how did you install Python 3.9? If not using conda with conda-forge, would you mind checking it?

I use homebrew, here's the output of `brew info python@3.9`:
```
python@3.9: stable 3.9.1 (bottled)
Interpreted, interactive, object-oriented programming language
https://www.python.org/
/usr/local/Cellar/python@3.9/3.9.1_6 (8,665 files, 129.0MB) *
  Built from source on 2021-01-10 at 20:57:51
```
I don't use conda (and I don't have conda installed on my system)

by the way, this seems to be an issue with conda-forge: https://github.com/conda-forge/python-feedstock/issues/436

I'm too busy to look into this deeply right now, but if I ever find anything interesting I'll update this thread. Let me know if you need any debugging output from me! 
I was able to at least `import vispy` under current-as-of-today conda-forge python 3.8.6 and 3.9.1 with this quick patch pieced together from above recommendations and observations, on macOS 11.1: 

```patch
diff --git a/vispy/ext/cocoapy.py b/vispy/ext/cocoapy.py
index d510d1ae..600d1870 100644
--- a/vispy/ext/cocoapy.py
+++ b/vispy/ext/cocoapy.py
@@ -106,7 +106,7 @@ if sizeof(c_void_p) == 4:
 elif sizeof(c_void_p) == 8:
     c_ptrdiff_t = c_int64
 
-objc = cdll.LoadLibrary(util.find_library('objc'))
+objc = cdll.LoadLibrary(util.find_library('objc') or 'libobjc.dylib')
 
 objc.class_addIvar.restype = c_bool
 objc.class_addIvar.argtypes = [c_void_p, c_char_p, c_size_t, c_uint8, c_char_p]
@@ -920,7 +920,7 @@ class ObjCSubclass(object):
 ##############################################################################
 # cocoalibs.py
 
-cf = cdll.LoadLibrary(util.find_library('CoreFoundation'))
+cf = cdll.LoadLibrary(util.find_library('CoreFoundation') or 'CoreFoundation.framework/CoreFoundation')
 
 kCFStringEncodingUTF8 = 0x08000100
 
@@ -1120,7 +1120,7 @@ cf.CFShow.argtypes = [c_void_p]
 
 # Even though we don't use this directly, it must be loaded so that
 # we can find the NSApplication, NSWindow, and NSView classes.
-appkit = cdll.LoadLibrary(util.find_library('AppKit'))
+appkit = cdll.LoadLibrary(util.find_library('AppKit') or 'AppKit.framework/AppKit')
 
 NSDefaultRunLoopMode = c_void_p.in_dll(appkit, 'NSDefaultRunLoopMode')
 NSEventTrackingRunLoopMode = c_void_p.in_dll(
@@ -1261,7 +1261,7 @@ NSApplicationActivationPolicyProhibited = 2
 
 # QUARTZ / COREGRAPHICS
 
-quartz = cdll.LoadLibrary(util.find_library('quartz'))
+quartz = cdll.LoadLibrary(util.find_library('quartz') or 'Quartz.framework/Quartz')
 
 CGDirectDisplayID = c_uint32     # CGDirectDisplay.h
 CGError = c_int32                # CGError.h
@@ -1431,7 +1431,7 @@ quartz.CGDisplayBounds.restype = CGRect
 ######################################################################
 
 # CORETEXT
-ct = cdll.LoadLibrary(util.find_library('CoreText'))
+ct = cdll.LoadLibrary(util.find_library('CoreText') or 'CoreText.framework/CoreText')
 
 # Types
 CTFontOrientation = c_uint32      # CTFontDescriptor.h
```


@k30n1 So theoretically, from what I understand of that code, that should be backwards compatible?

@goanpeca Do you think you could try the above changes in vispy and see if it fixes your issue? @astrofrog Does this work for you? Or (based on your other issue on conda-forge) do you see any issues with these patches?

@k30n1 Any chance I can get you to make a PR for this?

Resolving patch:
diff --git a/vispy/ext/cocoapy.py b/vispy/ext/cocoapy.py
--- a/vispy/ext/cocoapy.py
+++ b/vispy/ext/cocoapy.py
@@ -17,6 +17,27 @@
 else:
     string_types = basestring,  # noqa
 
+
+# handle dlopen cache changes in macOS 11 (Big Sur)
+# ref https://stackoverflow.com/questions/63475461/unable-to-import-opengl-gl-in-python-on-macos
+try:
+    import OpenGL.GL   # noqa
+except ImportError:
+    # print('Drat, patching for Big Sur')
+    orig_util_find_library = util.find_library
+    
+    def new_util_find_library(name):
+        res = orig_util_find_library(name)
+        if res:
+            return res
+        lut = {
+            'objc': 'libobjc.dylib',
+            'quartz': 'Quartz.framework/Quartz'
+        }
+        return lut.get(name, name+'.framework/'+name)
+    util.find_library = new_util_find_library
+
+
 # Based on Pyglet code
 
 ##############################################################################


Test patch: