快速变化目录的递归通配符匹配

3
我发现了一个不直观的问题:如果你使用pathlib递归地glob一个快速变化的目录,其中其他目录被创建和删除,你有可能会遇到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


你尝试过这样写吗?try: list(p.rglob('*')) except FileNotFoundException: continue - Jakob Guldberg Aaes
我需要所有可以提供rglob的选项,如果它被中断,我需要重新开始。 - nonForgivingJesus
由于听起来您需要速度,pathlib并不是最快的。如果您没有使用rglob的限制,请查看此链接https://github.com/juancarlospaco/thatlib#thatlib - Jakob Guldberg Aaes
也许这是服务器故障或Unix方面更熟悉的情况。 - diggusbickus
2个回答

2

引发异常的原语是os.scandir

发生的情况是pathlib.Path.rglob()方法创建了一个选择器对象(pathlib._WildcardSelector, 通过给定你的模式,经由pathlib._make_selector),然后在执行rglob()生成器期间对其进行惰性迭代。

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

最好的解决方案可能是子类化PathPosixPath类,重写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 版本上运行。


0
  • 添加另一个可能的解决方案
  • 如果您快速更改目录包含隐藏文件/目录(即在Unix文件系统中以.开头的目录)
  • 那么您可以使用glob.globglob.iglob而不是pathlib.Path.glob
  • glob.glob默认忽略隐藏文件/目录。因此,如果隐藏的文件/目录在扫描过程中被删除,它不会崩溃
  • 这将适用于使用文件分段创建隐藏文件的程序。例如Pyspark创建.spark-staging文件

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接