-
Notifications
You must be signed in to change notification settings - Fork 151
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
Allow a way to specify the portaudio location #130
Comments
Thanks for bringing this up. I was actually wondering that this didn't come up earlier. Note that since Python 3.6 the environment variable
I don't understand this "downside". Do you mean if PortAudio is already installed with the package manager? Doesn't
Well the problem is that some OSs don't have standard places or standard mechanisms for that. I've already tried to support a special case in #122, I can try to add others.
Sure, I'm open to a PR. BTW, did you try if other packaging tools work better, like e.g. PyInstaller? Would API mode (see #91) help? How are "traditional" compiled Python extension modules handled by py2app? |
The problem isn't the packaging tool itself (that part I can mostly work around). The issue is Apple's rules. To code-sign an app you must put all compiled libs into the Frameworks folder.
They are put into the frameworks folder.
On Mac I've found find_library() to be pretty dumb in its search BUT I've just realised that it will find the necessary lib if it's given the complete name (in this case I just needed to point to the correct full name of the version I was packaging and then it did find the lib in that Frameworks location).
So I think I can work with this; I'll just add a symlink during packaging I'll also need to ban |
I just created a PR for PyInstaller to bundle |
Thanks @j9ac9k! |
Thanks for making this library, you helped me regain my sanity with trying to replicate the Also in case you're curious, with this PR, |
Hi! I am using PyInstaller to bundle a Sanity ChecksI have made sure that
ExperimentI have tested the following within the frozen application:
This means, loading sounddevice fails due to two reasons:
That means that even @peircej's sym-link workaround would not work here. SolutionsTo solve this issue long-term, it would be great if one could manually specify the library name that is passed to My temporary workaround is to define a runtime hook that modifies import ctypes.util
import functools
print("Attempting to import sounddevice using patched `ctypes.util.find_library`...")
_find_library_original = ctypes.util.find_library
@functools.wraps(_find_library_original)
def _find_library_patched(name):
if name == "portaudio":
return "libportaudio.so.2"
else:
return _find_library_original(name)
ctypes.util.find_library = _find_library_patched
import sounddevice
print("sounddevice import successful!")
print("Restoring original `ctypes.util.find_library`...")
ctypes.util.find_library = _find_library_original
del _find_library_patched
print("Original `ctypes.util.find_library` restored.") |
I don't fully understand this ... maybe because I have never used PyInstaller ... When you talk about finding Can you please describe the layout of the files when using PyInstaller? I guess we could try to bundle a Linux binary, just like we do with macOS and Windows already, but I don't really know how to create such a library that is compatibly with most Linux systems. Another alternative would be to switch to API mode (see #91), and provide a Linux wheel. I don't know how that would interact with PyInstaller, though.
Yeah, that's what this issue is about, but so far I haven't found a "good" way to achieve this.
We could add it there, but if I understand you correctly, this wouldn't help anyway. |
@mgeier Hi, thank you for taking the time to respond and look into this issue! This is a shortened version of the PyInstaller Linux bundle file structure that I created:
PyInstaller repackages the files and ensures that the dynamic linking works as expected. Even if the wheel were included in the wheel, the corresponding loading code would need to be adjusted to not rely on
Correct, this is my understanding, too. No need to add it.
Other libraries, e.g. pyGLFW, look for the existence of a special environment variable. API mode is not an option as long as it does not support Windows and macOS, too. :-/
I can highly recommend https://cibuildwheel.readthedocs.io/en/stable/ It takes care of running the wheel packaging process on a corresponding manylinux image. Depending on the age of the selected manylinux image, you might need to use cibuildwheel also calls auditwheel which ensures that the wheel contains all needed c libraries. The corresponding macOS and Windows equivalents are https://github.com/matthew-brett/delocate and https://github.com/adang1345/delvewheel |
There is already a hook for PyInstaller https://github.com/pyinstaller/pyinstaller-hooks-contrib/blob/master/src/_pyinstaller_hooks_contrib/hooks/stdhooks/hook-sounddevice.py which mentions Linux (and which may be the reason why However, I don't know if those tests have actually be run on Linux ... probably not? I'm not sure what the search paths for The Python docs (https://docs.python.org/3.8/library/ctypes.html#finding-shared-libraries) mention I'm not sure if that feasible though, because how would you make sure your users have the environment variable set correctly? The Python docs link also says this: "If wrapping a shared library with ctypes, it may be better to determine the shared library name at development time, and hardcode that into the wrapper module instead of using I think it would be great to solve this a install time and not rely on an environment variable at runtime.
Yeah, that's a possibility, but I'm not sure how easy that is for users.
Sure, I would only do that if it supports Windows and macOS, and ideally also Raspberry Pi. Thanks for mentioning However, what I meant was a way to create the |
Hi @mgeier
|
Thanks for the detailed information!
I think I'm starting to accept that, thanks for providing the real-world examples. I originally had the feeling that this might add some overhead to all the cases where the feature is not needed, but I guess the environment variables are loaded by the Python interpreter anyway, so the cost would just be a dictionary lookup, right? Would you like to make a PR for this? |
Hey, yes, I have a few other things to do first, but I will try to find the time to contribute the implementation! |
For the record, I have documented how to use a custom PortAudio: #518 |
Regarding the system paths discussion in #496, I don't think it really matters whether it's a standard or custom location; either way it's probably not a good idea to require adding it to the default library search path, regardless of OS. That means that all apps/libraries which are searching for the same library will find the overridden one, which may or may not be what the user wants. I think to allow custom library dependencies it should be either in a sounddevice-specific directory (e.g. %AppData%\sounddevice\, ~/.sounddevice/, etc) or specified by environment variable as suggested, and either way should bypass the find_library, which should only be for actually system installed libraries. |
It would be nice to be able to specify the location of the portaudio lib for people that want to use a specific location and/or version. At present the code just uses whatever ctypes find_library discovers or reverts to the provided
_sounddevice_data
folder.Downsides:
I wonder if we could have something like a check for an environment variable first, like os.environ['SD_PORTAUDIO'] ?
The text was updated successfully, but these errors were encountered: