Skip to content

Commit 0c069cb

Browse files
authored
Add support for Zsh style *** (#225)
* Add support for Zsh style `***` * Add tests for glob * Use R'' for non regex strings * Add globmatch tests * Add pathlib tests and fix some issues - Add missing `PurePath.full_match` method that redirects to `PurePath.globmatch`. - `PurePath.match` should respect hidden file rules and symlink rules the same as `rglob`. `match` matches what `rglob` globs. * Fix mypy Python 3.13
1 parent 35e9cac commit 0c069cb

File tree

11 files changed

+591
-245
lines changed

11 files changed

+591
-245
lines changed

docs/src/dictionary/en-custom.txt

+1
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ preprocessing
6464
preprocessors
6565
prerelease
6666
prereleases
67+
recurse
6768
recursing
6869
regex
6970
sharepoint

docs/src/markdown/about/changelog.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@
22

33
## 10.0
44

5+
- **NEW**: Added `GLOBSTARLONG` which adds support for the Zsh style `***` which acts like `**` with `GLOBSTAR` but
6+
but traverses symlinks.
7+
- **NEW**: `pathlib.match` will respect symlinks (when the `REALPATH` flag is given) and hidden file rules (enable
8+
`DOTALL` to match hidden files).
59
- **NEW**: Symlinks should not be traversed when `GLOBSTAR` is enabled unless `FOLLOW` is also enabled, but they
610
should still be matched. Prior to this change, symlinks were not traversed _and_ they were ignored from matching
7-
which contradicts how Bash works, which is are general target.
11+
which contradicts how Bash works and could be confusing to users.
812
- **FIX**: Fix some inconsistencies with `globmatch` and symlink handling when `REALPATH` is enabled.
913

1014
## 9.0

docs/src/markdown/glob.md

+22-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ character `#!py3 r'\?'`. If you want to represent a literal backslash, you must
1919
Pattern | Meaning
2020
----------------- | -------
2121
`*` | Matches everything except slashes. On Windows it will avoid matching backslashes as well as slashes.
22-
`**` | Matches zero or more directories, but will never match the directories `.` and `..`. Requires the [`GLOBSTAR`](#globstar) flag.
22+
`**` | Matches zero or more directories, but will never match the directories ` . ` and `..`. Requires the [`GLOBSTAR`](#globstar) flag.
23+
`***` | Like `**` but will also recurse symlinks. Requires the [`GLOBSTARLONG`](#globstarlong) flag.
2324
`?` | Matches any single character.
2425
`[seq]` | Matches any character in seq.
2526
`[!seq]` | Matches any character not in seq. Will also accept character exclusions in the form of `[^seq]`.
@@ -766,10 +767,27 @@ When `MINUSNEGATE` is used with [`NEGATE`](#negate), exclusion patterns are reco
766767

767768
`GLOBSTAR` enables the feature where `**` matches zero or more directories.
768769

770+
#### `glob.GLOBSTARLONG, glob.GL` {: #globstarlong}
771+
772+
/// new | New 10.0
773+
///
774+
775+
When `GLOBSTARLONG` is enabled `***` will act like `**`, but will cause symlinks to be traversed as well.
776+
777+
Enabling `GLOBSTARLONG` automatically enables [`GLOBSTAR`](#globstar).
778+
779+
[`FOLLOW`](#follow) will be ignored and `***` will be required to traverse a symlink. But it should be noted that when
780+
using [`MATCHBASE`](#matchbase) and [`FOLLOW`](#follow) with `GLOBSTARLONG`, that [`FOLLOW`](#follow) will cause the
781+
implicit leading `**` that [`MATCHBASE`](#matchbase) applies to act as an implicit `***`.
782+
769783
#### `glob.FOLLOW, glob.L` {: #follow}
770784

771785
`FOLLOW` will cause [`GLOBSTAR`](#globstar) patterns (`**`) to traverse symlink directories.
772786

787+
`FOLLOW` will have no affect if using [`GLOBSTARLONG`](#globstarlong) and an explicit `***` will be required to traverse
788+
a symlink. `FOLLOW` will have an affect if enabled with [`GLOBSTARLONG`](#globstarlong) and [`MATCHBASE`](#matchbase)
789+
and will cause the implicit leading `**` that `MATCHBASE` applies to act as an implicit `***`.
790+
773791
#### `glob.REALPATH, glob.P` {: #realpath}
774792

775793
In the past, only [`glob`](#glob) and [`iglob`](#iglob) operated on the filesystem, but with `REALPATH`, other
@@ -786,6 +804,9 @@ logic so that the path must meet the following in order to match:
786804
- Path must exist.
787805
- Directories that are symlinks will not be traversed by [`GLOBSTAR`](#globstar) patterns (`**`) unless the
788806
[`FOLLOW`](#follow) flag is enabled.
807+
- If [`GLOBSTARLONG`](#globstarlong) is enabled, `***` will traverse symlinks, [`FOLLOW`](#follow) will be ignored
808+
except if [`MATCHBASE`](#matchbase) is also enabled, in that case, the implicit leading `**` added by
809+
[`MATCHBASE`](#matchbase) will act as `***`.
789810
- When presented with a pattern where the match must be a directory, but the file path being compared doesn't indicate
790811
the file is a directory with a trailing slash, the command will look at the filesystem to determine if it is a
791812
directory.

docs/src/markdown/pathlib.md

+39-15
Original file line numberDiff line numberDiff line change
@@ -252,22 +252,12 @@ pattern(s).
252252

253253
`match` mimics Python's `pathlib` version of `match`. Python's `match` uses a right to left evaluation. Wildcard Match
254254
emulates this behavior as well. What this means is that when provided with a path `some/path/name`, the patterns `name`,
255-
`path/name` and `some/path/name` will all match.
256-
257-
Because the path is evaluated right to left, dot files may not prevent matches when `DOTGLOB` is disabled.
258-
259-
```pycon3
260-
>>> from wcmatch import pathlib
261-
>>> pathlib.PurePath('.dotfile/file').match('file')
262-
True
263-
>>> pathlib.PurePath('../.dotfile/file').match('file')
264-
True
265-
```
255+
`path/name` and `some/path/name` will all match. Essentially, it matches what [`rglob`](#rglob) returns.
266256

267257
`match` does not access the filesystem, but you can force the path to access the filesystem if you give it the
268258
[`REALPATH`](#realpath) flag. We do not restrict this, but we do not enable it by default.
269-
[`REALPATH`](#realpath) simply forces the match to check the filesystem to see if the file exists and is a
270-
directory or not.
259+
[`REALPATH`](#realpath) simply forces the match to check the filesystem to see if the file exists, if it is a
260+
directory or not, and whether it is a symlink.
271261

272262
Since [`Path`](#path) is derived from [`PurePath`](#purepath), this method is also available in
273263
[`Path`](#path) objects.
@@ -302,8 +292,8 @@ a list of patterns. It will return a boolean indicating whether the objects file
302292

303293
`globmatch` does not access the filesystem, but you can force the path to access the filesystem if you give it the
304294
[`REALPATH`](#realpath) flag. We do not restrict this, but we do not enable it by default.
305-
[`REALPATH`](#realpath) simply forces the match to check the filesystem to see if the file exists and is a
306-
directory or not.
295+
[`REALPATH`](#realpath) simply forces the match to check the filesystem to see if the file exists, if it is a
296+
directory or not, and whether it is a symlink.
307297

308298
Since [`Path`](#path) is derived from [`PurePath`](#purepath), this method is also available in
309299
[`Path`](#path) objects.
@@ -323,6 +313,19 @@ True
323313
`exclude` parameter was added.
324314
///
325315

316+
#### `PurePath.full_match` {: #full_match}
317+
318+
/// new | new 10.0
319+
///
320+
321+
```py3
322+
def full_match(self, patterns, *, flags=0, limit=1000, exclude=None):
323+
```
324+
325+
Python 3.13 added the new `full_match` method to `PurePath` objects. Essentially, this does for normal `pathlib`
326+
what our existing `PurePath.globmatch` has been doing prior to Python 3.13. We've added an alias for
327+
`PurePath.full_match` that redirects to [`PurePath.globmatch`](#globmatch) for completeness.
328+
326329
#### `Path.glob` {: #glob}
327330

328331
```py3
@@ -446,10 +449,27 @@ When `MINUSNEGATE` is used with [`NEGATE`](#negate), exclusion patterns are reco
446449

447450
`GLOBSTAR` enables the feature where `**` matches zero or more directories.
448451

452+
#### `glob.GLOBSTARLONG, glob.GL` {: #globstarlong}
453+
454+
/// new | New 10.0
455+
///
456+
457+
When `GLOBSTARLONG` is enabled `***` will act like `**`, but will cause symlinks to be traversed as well.
458+
459+
Enabling `GLOBSTARLONG` automatically enables [`GLOBSTAR`](#globstar).
460+
461+
[`FOLLOW`](#follow) will be ignored and `***` will be required to traverse a symlink. But it should be noted that when
462+
using [`MATCHBASE`](#matchbase) and [`FOLLOW`](#follow) with `GLOBSTARLONG`, that [`FOLLOW`](#follow) will cause the
463+
implicit leading `**` that [`MATCHBASE`](#matchbase) applies to act as an implicit `***`.
464+
449465
#### `pathlib.FOLLOW, pathlib.L` {: #follow}
450466

451467
`FOLLOW` will cause `GLOBSTAR` patterns (`**`) to match and traverse symlink directories.
452468

469+
`FOLLOW` will have no affect if using [`GLOBSTARLONG`](#globstarlong) and an explicit `***` will be required to traverse
470+
a symlink. `FOLLOW` will have an affect if enabled with [`GLOBSTARLONG`](#globstarlong) and [`MATCHBASE`](#matchbase)
471+
and will cause the implicit leading `**` that `MATCHBASE` applies to act as an implicit `***`.
472+
453473
#### `pathlib.REALPATH, pathlib.P` {: #realpath}
454474

455475
In the past, only `glob` and `iglob` operated on the filesystem, but with `REALPATH`, other functions will now operate
@@ -466,6 +486,10 @@ that the path must meet the following in order to match:
466486
- Path must exist.
467487
- Directories that are symlinks will not be matched by [`GLOBSTAR`](#globstar) patterns (`**`) unless the
468488
[`FOLLOW`](#follow) flag is enabled.
489+
- If [`GLOBSTARLONG`](#globstarlong) is enabled, `***` will traverse symlinks, [`FOLLOW`](#follow) will be ignored
490+
except if [`MATCHBASE`](#matchbase) is also enabled, in that case, the implicit leading `**` added by
491+
[`MATCHBASE`](#matchbase) will act as `***`. This also affects the implicit leading `**` adding by
492+
[`rglob`](#rglob).
469493
- When presented with a pattern where the match must be a directory, but the file path being compared doesn't indicate
470494
the file is a directory with a trailing slash, the command will look at the filesystem to determine if it is a
471495
directory.

0 commit comments

Comments
 (0)