I'm running Twisted (Python 2.7.x) on Alpine Linux 3.7 inside Docker.
I now wanted to use the twisted.internet.inotify
module, but it fails loading.
It's triggering the following exception in twisted.python._inotify
:
name = ctypes.util.find_library('c')
if not name:
raise ImportError("Can't find C library.")
libc = ctypes.cdll.LoadLibrary(name)
initializeModule(libc)
The problem is that Alpine Linux 3.x has a bug which makes ctypes.util.find_library('c')
return None
.
I've compared the code with the inotify
module, which I've successfully used in Alpine before, and that one deals with the issue in the following way:
_FILEPATH = ctypes.util.find_library('c')
if _FILEPATH is None:
_FILEPATH = 'libc.so.6'
instance = ctypes.cdll.LoadLibrary(_FILEPATH)
So i've tried calling ctypes.util.find_library('libc.so.6')
in the interpreter, and that call succeeds.
What I now want to do is to monkey-patch twisted.python._inotify
so that it loads libc.so.6
instead of c
, but I'm unaware of how I can do that, because I can't load the module at all.
I have one option, which is to sed
the source code during docker build
, or possibly even inside the server right after it starts, but that feels like a hack.
I've seen that Twisted contains a MonkeyPatch module, but I have no idea on how to use it, or if it is even suited for this task.
How can I solve this problem in the cleanest possible way?
Note: The server is running as non-root, so it has no write access to /usr/lib/python2.7/site-packages/twisted/python/_inotify.py
.
This means that I either have to sed
it in the Dockerfile, or patch in in-memory when the server starts, before it loads the module (if that's possible, I'd prefer that).
In addition to anything else, I hope that you contribute a patch to Twisted to either solve this problem outright or make it easier to solve from application code or at an operations level.
That said, here's a monkey-patch that should do for you:
import ctypes.util
def fixed_find_library(name):
if name == "c":
result = original_find_library(name)
if result is not None:
return result
else:
return "libc.so.6"
return original_find_library(name)
original_find_library = ctypes.util.find_library
ctypes.util.find_library = fixed_find_library
# The rest of your application code...
This works simply by codifying the logic from your question which you suggest works around the problem. As long as this code runs before _inotify.py
is imported then when it does get imported it will end up using the "fixed" version instead of the original.