17

我有一个 urllib2 缓存模块,由于以下代码,它偶尔会崩溃:

if not os.path.exists(self.cache_location):
    os.mkdir(self.cache_location)

问题是,在执行第二行时,该文件夹可能已经存在,并且会报错:

  __init__ 中的文件“.../cache.py”,第 103 行
    os.mkdir(self.cache_location)
OSError:[Errno 17] 文件存在:'/tmp/examplecachedir/'

这是因为脚本多次同时启动,第三方代码我无法控制。

代码(在我尝试修复错误之前)可以在这里找到,在 github

我不能使用tempfile.mkstemp,因为它通过使用随机命名的目录(此处为 tempfile.py 源)来解决竞争条件,这会破坏缓存的目的。

我不想简单地丢弃该错误,因为如果文件夹名称作为文件存在(不同的错误),则会引发相同的错误 Errno 17 错误,例如:

$触摸废话
$蟒蛇
>>> 导入操作系统
>>> os.mkdir("废话")
回溯(最近一次通话最后):
  文件“”,第 1 行,在
OSError:[Errno 17] 文件存在:'blah'
>>>

我不能使用threading.RLock,因为代码是从多个进程中调用的。

因此,我尝试编写一个简单的基于文件的锁(可以在此处找到该版本),但这有一个问题:它会在上一级创建锁文件,因此/tmp/example.lockfor /tmp/example/,如果您/tmp/用作缓存目录(因为它尝试做/tmp.lock)..

简而言之,我需要将urllib2响应缓存到磁盘。为此,我需要以多进程安全的方式访问一个已知目录(如果需要,创建它)。它需要在 OS X、Linux 和 Windows 上运行。

想法?我能想到的唯一替代解决方案是使用 SQLite3 存储而不是文件重写缓存模块。

4

5 回答 5

12

代替

if not os.path.exists(self.cache_location):
    os.mkdir(self.cache_location)

你可以做

try:
    os.makedirs(self.cache_location)
except OSError:
    pass

因为你最终会得到相同的功能

免责声明:我不知道这可能是 Pythonic。


使用SQLite3,可能有点矫枉过正,但会为您的用例增加很多功能和灵活性。

如果您必须进行大量“选择”、并发插入和过滤,那么使用 是一个好主意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 会重新引发完全相同的异常(只是挑剔)


还有一件事,可能对您有用(仅限类 Unix)。

于 2009-10-19T02:00:27.300 回答
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
于 2011-11-19T07:25:30.880 回答
3

在 Python 3.x 中,您可以使用os.makedirs(path, exist_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)
于 2019-08-07T12:47:37.363 回答
2

你能捕捉到异常然后测试文件是否作为目录存在吗?

于 2009-10-19T01:57:54.090 回答
1

当你有比赛条件时,EAFP(请求宽恕比许可更容易)比 LBYL(在你跳跃之前看看)效果更好

错误检查策略

于 2009-10-19T02:49:29.493 回答