我有一个urllib2缓存模块,由于以下代码而偶尔会崩溃:
if not os.path.exists(self.cache_location):
os.mkdir(self.cache_location)
问题是,到第二行执行时,文件夹可能存在,并且会出错:
File ".../cache.py", line 103, in __init__ os.mkdir(self.cache_location) OSError: [Errno 17] File exists: '/tmp/examplecachedir/'
这是因为脚本是多次同时启动的,我无法控制第三方代码。
可以找到代码(在我尝试修复错误之前)here, on github
我无法使用tempfile.mkstemp,因为它通过使用随机命名的目录(tempfile.py source here)来解决竞争条件,这会破坏缓存的目的。
我不想简单地丢弃错误,因为如果文件夹名称作为文件存在(不同的错误),则会引发相同的错误Errno 17错误,例如:
$ touch blah $ python >>> import os >>> os.mkdir("blah") Traceback (most recent call last): File "", line 1, in OSError: [Errno 17] File exists: 'blah' >>>
我无法使用threading.RLock
,因为代码是从多个进程调用的。
所以,我尝试编写一个简单的基于文件的锁(that version can be found here),但这有一个问题:它创建一个级别的锁文件,所以/tmp/example.lock
为/tmp/example/
,如果您使用/tmp/
作为缓存目录(因为它尝试生成/tmp.lock
),则会中断。
简而言之,我需要将urllib2
个响应缓存到光盘。为此,我需要以多进程安全的方式访问已知目录(如果需要,创建它)。它需要在OS X,Linux和Windows上运行。
思考?我能想到的唯一替代解决方案是使用SQLite3存储而不是文件重写缓存模块。
答案 0 :(得分:11)
而不是
if not os.path.exists(self.cache_location):
os.mkdir(self.cache_location)
你可以做到
try:
os.makedirs(self.cache_location)
except OSError:
pass
因为你最终会得到相同的功能。
免责声明:我不知道Pythonic会是怎样的。
使用SQLite3
,可能有点过分,但会为您的用例添加 lot 的功能和灵活性。
如果你必须做很多“选择”,并发插入和过滤,那么使用SQLite3
是一个好主意,因为它不会在简单文件上添加太多复杂性(可以说它会删除它复杂性)。
重读你的问题(和评论)我可以更好地理解你的问题。
文件可能会产生相同的竞争条件?
如果它足够小,那么我会做类似的事情:
if not os.path.isfile(self.cache_location):
try:
os.makedirs(self.cache_location)
except OSError:
pass
另外,阅读你的代码,我会改变
else:
# Our target dir is already a file, or different error,
# relay the error!
raise OSError(e)
到
else:
# Our target dir is already a file, or different error,
# relay the error!
raise
因为它真的是你想要的,Python要重新加注完全相同的异常(只是挑剔)。
还有一件事,this可能对您有用(仅限Unix)。
答案 1 :(得分:10)
我最终得到的代码是:
import os
import errno
folder_location = "/tmp/example_dir"
try:
os.mkdir(folder_location)
except OSError as e:
if e.errno == errno.EEXIST and os.path.isdir(folder_location):
# File exists, and it's a directory,
# another process beat us to creating this dir, that's OK.
pass
else:
# Our target dir exists as a file, or different error,
# reraise the error!
raise
答案 2 :(得分:3)
在Python 3.x中,您可以使用os.makedirs(path, exists_ok=True)
,如果存在该目录,则不会引发任何异常。如果文件与请求的目录(FileExistsError: [Errno 17]
)同名,它将引发path
。
通过以下方式进行验证:
import os
parent = os.path.dirname(__file__)
target = os.path.join(parent, 'target')
os.makedirs(target, exist_ok=True)
os.makedirs(target, exist_ok=True)
os.rmdir(target)
with open(target, 'w'):
pass
os.makedirs(target, exist_ok=True)
答案 3 :(得分:2)
您是否可以捕获该异常,然后测试该文件是否作为目录存在?
答案 4 :(得分:2)
当你有竞争条件时,EAFP(更容易请求宽恕而不是许可)比LBYL更有效(在你跳跃之前看看)