I'm writing a pure Python library, and for various reasons I would very much like to avoid asking users to install any binary extensions. However, when running on OS X, I would also like to locate the user library directory ( ~/Library
) so I can store some configuration data there, and my understanding is that for Very Valid And Vague But Important Reasons the proper way to do this is not by just writing ~/Library
in my code, but instead by asking OS X where the directory is with some code like
[NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,
NSUserDomainMask,
YES)
objectAtIndex:0];
Of course, this code is Objective-C, not Python, so I can't just use it directly. And if it were plain C, I'd just use ctypes
to call it from Python, but it isn't. Is there any way to make this call from Python, without writing an extension module in Objective-C or requiring the user to install some extension module like PyObjC
? Alternatively, if I just give up and hard-code ~/Library
like everyone else does, then will anything terrible happen?
Well, it is plain C under the hood, so you can achieve the same result with ctypes
module:
from ctypes import *
NSLibraryDirectory = 5
NSUserDomainMask = 1
def NSSearchPathForDirectoriesInDomains(directory, domainMask, expand = True):
# If library path looks like framework, OS X will search $DYLD_FRAMEWORK_PATHs automatically
# There's no need to specify full path (/System/Library/Frameworks/...)
Foundation = cdll.LoadLibrary("Foundation.framework/Foundation")
CoreFoundation = cdll.LoadLibrary("CoreFoundation.framework/CoreFoundation");
_NSSearchPathForDirectoriesInDomains = Foundation.NSSearchPathForDirectoriesInDomains
_NSSearchPathForDirectoriesInDomains.argtypes = [ c_uint, c_uint, c_bool ]
_NSSearchPathForDirectoriesInDomains.restype = c_void_p
_CFRelease = CoreFoundation.CFRelease
_CFRelease.argtypes = [ c_void_p ]
_CFArrayGetCount = CoreFoundation.CFArrayGetCount
_CFArrayGetCount.argtypes = [ c_void_p ]
_CFArrayGetCount.restype = c_uint
_CFArrayGetValueAtIndex = CoreFoundation.CFArrayGetValueAtIndex
_CFArrayGetValueAtIndex.argtypes = [ c_void_p, c_uint ]
_CFArrayGetValueAtIndex.restype = c_void_p
_CFStringGetCString = CoreFoundation.CFStringGetCString
_CFStringGetCString.argtypes = [ c_void_p, c_char_p, c_uint, c_uint ]
_CFStringGetCString.restype = c_bool
kCFStringEncodingUTF8 = 0x08000100
# MAX_PATH on POSIX is usually 4096, so it should be enough
# It might be determined dynamically, but don't bother for now
MAX_PATH = 4096
result = []
paths = _NSSearchPathForDirectoriesInDomains(directory, domainMask, expand)
# CFArrayGetCount will crash if argument is NULL
# Even though NSSearchPathForDirectoriesInDomains never returns null, we'd better check it
if paths:
for i in range(0, _CFArrayGetCount(paths)):
path = _CFArrayGetValueAtIndex(paths, i)
buff = create_string_buffer(MAX_PATH)
if _CFStringGetCString(path, buff, sizeof(buff), kCFStringEncodingUTF8):
result.append(buff.raw.decode('utf-8').rstrip('\0'))
del buff
_CFRelease(paths)
return result
print NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask)
But the universe probably won't collapse if you just use ~/Library
;)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.