在OSX上,是否可以从纯Python获取用户库目录?
我正在编写一个纯Python库,出于各种原因,我非常希望避免要求用户安装任何二进制扩展。但是,在OS X上运行时,我还希望找到用户库目录(在OSX上,是否可以从纯Python获取用户库目录?,python,objective-c,macos,Python,Objective C,Macos,我正在编写一个纯Python库,出于各种原因,我非常希望避免要求用户安装任何二进制扩展。但是,在OS X上运行时,我还希望找到用户库目录(~/library),以便在那里存储一些配置数据,我的理解是,出于非常有效和模糊但重要的原因,正确的方法不是只在代码中写入~/library,而是用一些代码询问OSX目录在哪里,比如 [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,
~/library
),以便在那里存储一些配置数据,我的理解是,出于非常有效和模糊但重要的原因,正确的方法不是只在代码中写入~/library
,而是用一些代码询问OSX目录在哪里,比如
[NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,
NSUserDomainMask,
YES)
objectAtIndex:0];
当然,这段代码是Objective-C,不是Python,所以我不能直接使用它。如果它是纯C,我就用ctypes
从Python调用它,但它不是。有没有什么方法可以从Python调用,而不用在Objective-C中编写扩展模块或要求用户安装一些扩展模块,比如PyObjC
?或者,如果我只是放弃,像其他人一样硬编码~/Library
,那么会发生什么可怕的事情吗?好吧,引擎盖下是纯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)
但是如果你只使用~/Library
;),宇宙可能不会崩溃 嗯,引擎盖下面是普通的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)
但是如果你只使用
~/Library
;),宇宙可能不会崩溃 天哪,这太棒了,谢谢你——我很抱歉,你可能无法获得应有的声誉!另外,您是否愿意在许可的开源许可证(例如MIT或CC0)下许可此代码?这将为我在重新实现它时省去一点麻烦:-)我对麻省理工学院很好。毕竟,这只是一个例子,这里没有火箭科学:)@DagHøidahl:我知道——CC-BY-SA 3.0不适用于我需要它的项目,这就是为什么我问:-)。(事实上,我不知道有哪一个广泛使用的软件许可证与CC-BY-SA 3.0兼容。从某种意义上说,这对Stackoverflow来说是一个非常糟糕的选择;基本上,Stackoverflow上没有一个足够大/有创意且可版权保护的软件许可证可以合法复制到除玩具个人项目以外的任何项目中。)天哪,这太神奇了,谢谢你,我很抱歉,你可能得不到应有的声誉!另外,您是否愿意在许可的开源许可证(例如MIT或CC0)下许可此代码?这将为我在重新实现它时省去一点麻烦:-)我对麻省理工学院很好。毕竟,这只是一个例子,这里没有火箭科学:)@DagHøidahl:我知道——CC-BY-SA 3.0不适用于我需要它的项目,这就是为什么我问:-)。(事实上,我不知道有哪一个广泛使用的软件许可证与CC-BY-SA 3.0兼容。从某种意义上说,这对Stackoverflow来说是一个非常糟糕的选择;基本上,Stackoverflow上没有任何一个足够大/有创意且可版权保护的软件许可证可以合法复制到除玩具个人项目以外的任何项目中。)