Open mattgwwalker opened 4 years ago
I have a possible solution! Effectively, I've taken the current approach in library_loader.py
and prefixed it with a "load internal library" stage.
So using this approach, when a library is to be loaded, we specify the filename. Names for Windows and MacOS would need to be given, plus the general library name if the internal load fails and we attempt to load it externally. I looks like:
names = {
"win32": "opus.dll",
"darwin": "libopus.dylib",
"external": "opus"
}
libopus = Library.load(names, tests = [lambda lib: hasattr(lib, "opus_encoder_get_size")])
And then the library_loader.py
file gets an "internal loader" added in:
class Library:
@staticmethod
def load(names, paths = None, tests = []):
lib = InternalLibrary.load(names, tests)
if lib is None:
lib = ExternalLibrary.load(names["external"], paths, tests)
return lib
class InternalLibrary:
def load(names, tests):
# Get the name of the library for the current platform
try:
name = names[sys.platform]
except KeyError:
return None
# Attempt to load the library from here
path = _here + "/" + name
try:
lib = ctypes.CDLL(path)
except OSError:
return None
# Check that the library passes the tests
if tests and all(run_tests(lib, tests)):
return lib
return None
What do you think? Would you like this contribution?
Unfortunately, it appears that this is only a partial solution.
It works for libraries such as ogg and opus. Unfortunately, in its current form it's very much not a solution for the likes of opusfile, which seems to depend internally on ogg, for example.
I'll keep looking into this, but if you have any suggestions, I'm all ears.
I now have a working solution based on the above technique and the use of delocate, which repairs the internals of the dylibs that PyOgg requires (such that the system loads them from the PyOgg library and doesn't search for them in the standard system locations). I've tested it on OSX 10.13 and 10.15.
@Zuzu-Typ I'm not sure what the best approach is for sharing/documenting the process. Do you want to store the dylibs in the git repository? I can't see the DLLs stored there, so perhaps you'd prefer a different approach.
The technique even works when PyOgg is bundled by PyInstaller.
PyInstaller, however, doesn't automatically find the libraries (for that we'd have to go to string literals inside the ctypes calls, and I think that'd be a Bad Idea). But if the dylibs are specifically passed in using --add-binary and the dylibs are located inside the default directory ('.'), then all goes well.
Ha! Better yet, PyInstaller has a library of hooks that it uses to know what to do with specific Python distributions, and they're open to submissions. The hook needed for PyOgg is all of two lines. Then everything would just work whenever a user tried bundling their PyOgg-using application with PyInstaller.
The hook file is just these lines:
from PyInstaller.utils.hooks import collect_dynamic_libs
binaries = collect_dynamic_libs("pyogg")
The continuous integration tests are working under macOS using the pre-compiled binaries that are now included in the git repository. In order to tick off this issue we need to:
I've been struggling for most of today to create a PyOgg wheel for macOS.
I cannot work out how to specify a path to
ctypes.util.find_library()
under macOS. As a consequence, I can't work out how to replicate the current approach (working under Windows) of adding DLLs to the directory containing the Python source files.I've asked the question on Stack Overflow but if you have any suggestions, I'd love to hear them!
Cheers,
Matthew