]> git.ipfire.org Git - thirdparty/Python/cpython.git/commit
GH-102613: Fast recursive globbing in `pathlib.Path.glob()` (GH-104512)
authorBarney Gale <barney.gale@gmail.com>
Tue, 6 Jun 2023 22:50:36 +0000 (23:50 +0100)
committerGitHub <noreply@github.com>
Tue, 6 Jun 2023 22:50:36 +0000 (23:50 +0100)
commit24af45172f74e4f01eb21d3aee7beab62417b833
treef4679083343245b4cdc0e35eb9ce2859ba24b66d
parent2587b9f64eefde803a5e0b050171ad5f6654f31b
GH-102613: Fast recursive globbing in `pathlib.Path.glob()` (GH-104512)

This commit introduces a 'walk-and-match' strategy for handling glob patterns that include a non-terminal `**` wildcard, such as `**/*.py`. For this example, the previous implementation recursively walked directories using `os.scandir()` when it expanded the `**` component, and then **scanned those same directories again** when expanded the `*.py` component. This is wasteful.

In the new implementation, any components following a `**` wildcard are used to build a `re.Pattern` object, which is used to filter the results of the recursive walk. A pattern like `**/*.py` uses half the number of `os.scandir()` calls; a pattern like `**/*/*.py` a third, etc.

This new algorithm does not apply if either:

1. The *follow_symlinks* argument is set to `None` (its default), or
2. The pattern contains `..` components.

In these cases we fall back to the old implementation.

This commit also replaces selector classes with selector functions. These generators directly yield results rather calling through to their successors. A new internal `Path._glob()` method takes care to chain these generators together, which simplifies the lazy algorithm and slightly improves performance. It should also be easier to understand and maintain.
Doc/library/pathlib.rst
Lib/pathlib.py
Lib/test/test_pathlib.py
Misc/NEWS.d/next/Library/2023-05-15-18-57-42.gh-issue-102613.YD9yx-.rst [new file with mode: 0644]