快速变化目录的递归通配

Recursive globbing of fast-changing directories

我偶然发现了一个不直观的问题:如果你递归地 glob(使用 pathlib)快速变化的目录,在其他目录被创建和删除的地方,你有机会得到 FileNotFoundException。如果找不到目录,那为什么要用这个来打扰我呢?为什么不跳过虚假目录并继续呢?我如何以紧凑的方式解决这个问题?

重现方法如下:

用这个 sh 命令启动一个终端

while true; do mkdir deleteme && rmdir deleteme; done

另一个使用这个 python 脚本:

from pathlib import Path
p = Path()
while True:
    list(p.rglob('*'))

或者,作为一个班轮:

while true; do mkdir 1 && rmdir 1; done & python3 -c "from pathlib import Path; from itertools import count; p = Path(); [list(p.rglob('*')) for i in count(start=1)]"; fg

产生异常的原语是os.scandir

发生的事情是 pathlib.Path.rglob() 方法创建一个选择器对象(pathlib._WildcardSelector 通过 pathlib._make_selector,给定你的模式),然后在执行rglob() 生成器。

pathlib 模块似乎没有提供任何挂钩来轻松自定义或覆盖此行为。

最好的解决方案可能是子class PathPosixPath class,覆盖 rglob() 方法,在对这种错误模式有弹性的方式。

似乎可以作为概念证明的猴子补丁方法:

import contextlib
import pathlib

def my_scandir(path=None):
     while True:
         try:
             result = list(os.scandir(path))
         except FileNotFoundError:
             continue
         @contextlib.contextmanager
         def wrapped_result():
             try:
                 yield result
             finally:
                 pass
         return wrapped_result()

pathlib._NormalAccessor.scandir = staticmethod(my_scandir)

应用此补丁后,示例中带有 rglob('*') 的 while 循环不再因 FileNotFoundError.

而失败

这适用于 Python v3.8.5。因为它使用了 pathlib 模块的实现细节,所以它可能不适用于其他 python 版本。