[英]On OS X, is it possible to get the user library directory from pure Python?
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. 我正在编写一个纯Python库,出于各种原因,我非常想避免要求用户安装任何二进制扩展名。 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 但是,在OS X上运行时,我也想找到用户库目录(
~/Library
),以便在其中存储一些配置数据,我的理解是,对于“非常有效且含糊但重要的原因”,正确的做法是这不仅仅是通过在我的代码中编写~/Library
,而是通过询问OS X目录中包含一些代码的地方,例如
[NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,
NSUserDomainMask,
YES)
objectAtIndex:0];
Of course, this code is Objective-C, not Python, so I can't just use it directly. 当然,此代码是Objective-C,而不是Python,因此我不能直接使用它。 And if it were plain C, I'd just use
ctypes
to call it from Python, but it isn't. 如果它是纯C语言,我只会使用
ctypes
从Python调用它,但事实并非如此。 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
? 有什么方法可以从Python进行此调用,而无需在Objective-C中编写扩展模块或要求用户安装某些扩展模块(如
PyObjC
? Alternatively, if I just give up and hard-code ~/Library
like everyone else does, then will anything terrible happen? 或者,如果我像其他所有人一样放弃并硬编码
~/Library
,那么会发生什么可怕的事情吗?
Well, it is plain C under the hood, so you can achieve the same result with ctypes
module: 好吧, 它是底层的普通C语言,因此您可以使用
ctypes
模块获得相同的结果:
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
;) 但是如果只使用
~/Library
,宇宙可能不会崩溃。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.