奇怪的竞争条件:使用mkdtemp时出现FileNotFoundError
我在Mac OS X上用Python(我只测试了Python 3.3)遇到了一个很奇怪的竞态条件。我创建了几个临时目录,往里面写东西,然后再清理它们。大概是这样的:
while running:
(do something)
tempdir = mkdtemp('name')
try:
(write some stuff to tempdir)
finally:
shutil.rmtree(tempdir)
不过,在后面的几次循环中,当我尝试(往临时目录写东西)
时,出现了一些错误,比如:
with open(os.path.join("/var/folders/yc/8wpl9rlx47qgzxqpcf003k280000gn/T/tmp0fh2ztname", "file"), 'w', encoding='utf-8') as fn:
FileNotFoundError: [Errno 2] No such file or directory: '/var/folders/yc/8wpl9rlx47qgzxqpcf003k280000gn/T/tmpups5dpname/file'
(为了让大家更清楚,我把临时目录的路径直接写在了错误信息里)
注意,打开的路径和找不到的路径是不一样的。在每个错误信息中,显示的路径都是上一次循环中使用的临时目录。
这个错误大多数时候在同一个地方复现(大约在第四次循环后),但并不是每次都会出现。
补充说明:我刚意识到这可能很重要。(往临时目录写东西)
的操作实际上是在一个子进程中进行的。这就是我能确定临时目录路径的原因,因为我必须把它传递给子进程(我其实在说“为了清晰”时有点撒谎,实际上我是在写一个包含那行with open
的Python文件)。这就是我确定临时目录路径和正在使用的路径确实不同的原因。
1 个回答
我搞明白了。原来这和mkdtemp没有关系(松了一口气,看来Mac OS X和Python在这方面做得不错)。
问题在于,我把代码写到了一个文件里,包括这段 with open(os.path.join("/var/folders/yc/8wpl9rlx47qgzxqpcf003k280000gn/T/tmp0fh2ztname", "file"), 'w', encoding='utf-8') as fn:
,然后在一个子进程中运行。问题是我每次都在用 同一个 文件,而.pyc文件没有被正确更新。
错误信息让人困惑,因为当Python生成错误追踪信息时,它读取的是 .py
文件(也就是实际的代码所在),但实际上运行的是 .pyc
文件。
如果我理解 http://nedbatchelder.com/blog/200804/the_structure_of_pyc_files.html 正确的话,.pyc
文件中的时间戳只有一秒的精度(这也解释了为什么每次在同一个地方都能重现这个问题:循环中的同一个第四项在一秒内完成)。
解决办法是在写出文件时明确删除 .pyc 文件(在其他情况下,你也可以写到一个临时文件中,但在我的情况下,我需要这个文件能用同样的名字导入)。
大致是这样的:
if sys.version_info >= (3,):
os.unlink(os.path.join(path_to_file, '__pycache__', 'file.cpython-%s%s.pyc' % sys.version_info[:2]))
os.unlink(os.path.join(path_to_file, '__pycache__', 'file.cpython-%s%s.pyo' % sys.version_info[:2]))
else:
os.unlink(os.path.join(path_to_file, 'file.pyc'))
os.unlink(os.path.join(path_to_file, 'file.pyo'))