为 os.DirEntry 添加类型提示

Add type hint for os.DirEntry

我正在努力向接受 os.DirEntry 对象的函数添加类型提示(这些由 os.scandir() 生成)。这是一个接受 DirEntry 个对象的简单访问器 class:

class FileSystemVisitor:
    def visit_dir(self, entry) -> None:
        ...
    def visit_file(self, entry) -> None:
        ...

FileSystemVisitor 的一个实例被提供给遍历给定目录的子树的 visit() 函数:

def traverse(path: Union[str, pathlib.Path], visitor: FileSystemVisitor) -> None:
    for entry in os.scandir(str(path)):
        if entry.is_dir(follow_symlinks=False):
            visitor.visit_dir(entry)
            traverse(entry.path, visitor)
        else:
            visitor.visit_file(entry)

如何为 FileSystemVisitor.visit_{dir(),file()} 函数中的 entry 参数添加类型提示?为此,我无法导入 DirEntry

$ python3.5 -c "from os import DirEntry"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
ImportError: cannot import name 'DirEntry'

我能想到的一件事是编写一个模仿 DirEntry 的虚拟 class 并将其用于类型提示:

class DirEntryType:
    name = None  # type: str
    path = None  # type: str

    def inode(self) -> int:
        ...
    def is_dir(self) -> bool:
        ...
    def is_file(self) -> bool:
        ...
    def is_symlink(self) -> bool:
        ...
    def stat(self) -> os.stat_result:
        ...

但是,添加整个 class 只是为了类型提示那么聪明吗?

如果这很重要,我坚持使用 python3.5,因此 python3.6 的功能不可用。


编辑

正如avigil在评论中指出的那样,DirEntry可以在python3.6中导入:

$ python3.6 -c "from os import DirEntry; print(DirEntry)"
<class 'posix.DirEntry'>

因此,向后兼容的解决方案可以是例如:

# typing_utils.py

class DirEntryStub:
    name = None  # type: str
    path = None  # type: str

    def inode(self) -> int:
        raise NotImplementedError('This class is used for type hints only')
    def is_dir(self, follow_symlinks: bool = False) -> bool:
        raise NotImplementedError('This class is used for type hints only')
    def is_file(self, follow_symlinks: bool = False) -> bool:
        raise NotImplementedError('This class is used for type hints only')
    def is_symlink(self) -> bool:
        raise NotImplementedError('This class is used for type hints only')
    def stat(self) -> os.stat_result:
        raise NotImplementedError('This class is used for type hints only')

现在我可以输入 FileSystemVisitor:

try:
    from os import DirEntry
except ImportError:
    from typing_utils import DirEntryStub as DirEntry

class FileSystemVisitor:
    def visit_dir(self, entry: DirEntry) -> None:
        ...
    def visit_file(self, entry: DirEntry) -> None:
        ...

DirEntry 在 posix 模块中用 C 语言实现,但不幸的是,直到 3.6 版本才在 python 中公开。有关 python 错误跟踪器问题,请参阅 bpo-27038

对于较早的版本,您可以按照您的建议进行操作并将其存根,除非您足够关心编译您自己的 patched version. This actually wouldn't be too hard since the scandir implementation comes originally from the scandir 程序包,该程序包可以被修补并作为依赖项引入标准库实现。