Skip to content

Commit 73e0f45

Browse files
authored
Add option to get a precompiled matcher object (#227)
1 parent 0af3bc5 commit 73e0f45

File tree

17 files changed

+433
-131
lines changed

17 files changed

+433
-131
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ https://facelessuser.github.io/wcmatch/
6161
MIT
6262

6363

64-
[github-ci-image]: https://github.com/facelessuser/wcmatch/workflows/build/badge.svg?branch=main&event=push
64+
[github-ci-image]: https://github.com/facelessuser/wcmatch/workflows/build/badge.svg
6565
[github-ci-link]: https://github.com/facelessuser/wcmatch/actions?query=workflow%3Abuild+branch%3Amain
6666
[codecov-image]: https://img.shields.io/codecov/c/github/facelessuser/wcmatch/main.svg?logo=codecov&logoColor=aaaaaa&labelColor=333333
6767
[codecov-link]: https://codecov.io/github/facelessuser/wcmatch

docs/src/dictionary/en-custom.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ MkDocs
1717
NONINFRINGEMENT
1818
POSIX
1919
Pathlib
20+
Precompiling
2021
Preprocess
2122
PyPI
2223
Setuptools
@@ -58,6 +59,8 @@ paren
5859
pathlike
5960
posix
6061
pre
62+
precompile
63+
precompiled
6164
prepend
6265
prepends
6366
preprocessing

docs/src/markdown/about/changelog.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Changelog
22

3+
## 10.1
4+
5+
- **NEW**: Add `wcmatch.glob.compile(pattern)` and `wcmatch.fnmatch.compile(pattern)` to allow for precompiled matcher
6+
objects that can be reused.
7+
38
## 10.0
49

510
- **NEW**: Added `GLOBSTARLONG` which adds support for the Zsh style `***` which acts like `**` with `GLOBSTAR` but
@@ -80,7 +85,7 @@
8085
- **NEW**: `fnmatch` now has `escape` available via its API. The `fnmatch` variant uses filename logic instead of path
8186
logic.
8287
- **NEW**: Deprecate `raw_escape` in `glob` as it is very niche and the same can be accomplished simply by using
83-
`#!py3 codecs.decode(string, 'unicode_escape')` and then using `escape`.
88+
`codecs.decode(string, 'unicode_escape')` and then using `escape`.
8489
- **FIX**: Use `os.fspath` to convert path-like objects to string/bytes, whatever the return from `__fspath__` is what
8590
Wildcard Match will accept. Don't try to convert paths via `__str__` or `__bytes__` as not all path-like objects may
8691
implement both.

docs/src/markdown/about/release.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ The [`WcMatch`](../wcmatch.md#wcmatch) class `on_init` hook was cleaned up. Prio
1313
Moving forward, the `WcMatch` class will restrict all parameters to `**kwargs`. If you are using the `on_init` hook,
1414
you will simply need to change your override to accept arguments as `**kwargs`:
1515

16-
```py3
16+
```py
1717
# Excplicitly named
1818
def on_init(self, key1=value, key2=value):
1919

@@ -23,7 +23,7 @@ def on_init(self, **kwargs):
2323

2424
Lastly, only pass your custom variables in as keyword arguments:
2525

26-
```py3
26+
```py
2727
CustomWcmatch('.', '*.md|*.txt', flags=wcmatch.RECURSIVE, custom_key=value)
2828
```
2929

docs/src/markdown/fnmatch.md

Lines changed: 81 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# `wcmatch.fnmatch`
22

3-
```py3
3+
```py
44
from wcmatch import fnmatch
55
```
66

@@ -13,7 +13,7 @@ default. See [flags](#flags) for more information.
1313

1414
/// tip | Backslashes
1515
When using backslashes, it is helpful to use raw strings. In a raw string, a single backslash is used to escape a
16-
character `#!py3 r'\?'`. If you want to represent a literal backslash, you must use two: `#!py3 r'some\\path'`.
16+
character `#!py r'\?'`. If you want to represent a literal backslash, you must use two: `#!py r'some\\path'`.
1717
///
1818

1919
Pattern | Meaning
@@ -51,11 +51,30 @@ be no limit.
5151
The imposed pattern limit and corresponding `limit` option was introduced in 6.0.
5252
///
5353

54+
## Precompiling
55+
56+
While patterns are often cached, auto expanding patterns, such as `'file{a, b, c}'` will have each individual
57+
permutation cached (up to the cache limit), but not the entire pattern. This is to prevent the cache from exploding with
58+
really large patterns such as `{1..100}`. Essentially, individual patterns are cached, but not the expansion of a
59+
pattern into many patterns.
60+
61+
If it is planned to reuse a pattern and the performance hit of recompiling is not desired, you can precompile a matcher
62+
object via [`fnmatch.compile`](#compile) which returns a [`WcMatcher`](#wcmatcher) object.
63+
64+
```py
65+
>>> import wcmatch.fnmatch as fnmatch
66+
>>> m = fnmatch.compile('*.md')
67+
>>> m.match('README.md')
68+
True
69+
>>> m.filter(['test.txt', 'file.md', 'README.md'])
70+
['file.md', 'README.md']
71+
```
72+
5473
## API
5574

5675
#### `fnmatch.fnmatch` {: #fnmatch}
5776

58-
```py3
77+
```py
5978
def fnmatch(filename, patterns, *, flags=0, limit=1000, exclude=None)
6079
```
6180

@@ -128,7 +147,7 @@ True
128147

129148
#### `fnmatch.filter` {: #filter}
130149

131-
```py3
150+
```py
132151
def filter(filenames, patterns, *, flags=0, limit=1000, exclude=None):
133152
```
134153

@@ -151,9 +170,60 @@ pattern or a list of patterns.It returns a list of all files that matched the pa
151170
`exclude` parameter was added.
152171
///
153172

173+
#### `fnmatch.compile` {: #compile}
174+
175+
```py
176+
def compile(patterns, *, flags=0, limit=1000, exclude=None):
177+
```
178+
179+
The `compile` function takes a file pattern (or list of patterns) and flags. It also allows configuring the [max pattern
180+
limit](#multi-pattern-limits). Exclusion patterns can be specified via the `exclude` parameter which takes a pattern or
181+
a list of patterns. It returns a [`WcMatcher`](#wcmatcher) object which can match or filter file paths depending on
182+
which method is called.
183+
184+
```pycon3
185+
>>> import wcmatch.fnmatch as fnmatch
186+
>>> m = fnmatch.compile('*.md')
187+
>>> m.match('README.md')
188+
True
189+
>>> m.filter(['test.txt', 'file.md', 'README.md'])
190+
['file.md', 'README.md']
191+
```
192+
193+
#### `fnmatch.WcMatcher` {: #wcmatcher}
194+
195+
The `WcMatcher` class is returned when a pattern is precompiled with [`compile`](#compile). It has two methods: `match`
196+
and `filter`.
197+
198+
```py
199+
def match(self, filename):
200+
```
201+
202+
This `match` method allows for matching against a precompiled pattern.
203+
204+
```pycon3
205+
>>> import wcmatch.fnmatch as fnmatch
206+
>>> m = fnmatch.compile('*.md')
207+
>>> m.match('README.md')
208+
True
209+
```
210+
211+
```py
212+
def filter(self, filenames):
213+
```
214+
215+
The `filter` method allows for filtering paths against a precompiled pattern.
216+
217+
```pycon3
218+
>>> import wcmatch.fnmatch as fnmatch
219+
>>> m = fnmatch.compile('*.md')
220+
>>> m.filter(['test.txt', 'file.md', 'README.md'])
221+
['file.md', 'README.md']
222+
```
223+
154224
#### `fnmatch.translate` {: #translate}
155225

156-
```py3
226+
```py
157227
def translate(patterns, *, flags=0, limit=1000, exclude=None):
158228
```
159229

@@ -173,8 +243,8 @@ matches at least one inclusion pattern and matches **none** of the exclusion pat
173243

174244
When using [`EXTMATCH`](#extmatch) patterns, patterns will be returned with capturing groups around the groups:
175245

176-
While in regex patterns like `#!py3 r'(a)+'` would capture only the last character, even though multiple where matched,
177-
we wrap the entire group to be captured: `#!py3 '+(a)'` --> `#!py3 r'((a)+)'`.
246+
While in regex patterns like `#!py r'(a)+'` would capture only the last character, even though multiple where matched,
247+
we wrap the entire group to be captured: `#!py '+(a)'` --> `#!py r'((a)+)'`.
178248

179249
```pycon3
180250
>>> from wcmatch import fnmatch
@@ -199,7 +269,7 @@ Translate patterns now provide capturing groups for [`EXTMATCH`](#extmatch) grou
199269

200270
#### `fnmatch.escape` {: #escape}
201271

202-
```py3
272+
```py
203273
def escape(pattern):
204274
```
205275

@@ -220,7 +290,7 @@ An `escape` variant for `fnmatch` was made available in 8.1.
220290

221291
### `fnmatch.is_magic` {: #is_magic}
222292

223-
```py3
293+
```py
224294
def is_magic(pattern, *, flags=0):
225295
"""Check if the pattern is likely to be magic."""
226296
```
@@ -267,8 +337,8 @@ Added `is_magic` in 8.1.
267337

268338
#### `fnmatch.RAWCHARS, fnmatch.R` {: #rawchars}
269339

270-
`RAWCHARS` causes string character syntax to be parsed in raw strings: `#!py3 r'\u0040'` --> `#!py3 r'@'`. This will
271-
handle standard string escapes and Unicode including `#!py3 r'\N{CHAR NAME}'`.
340+
`RAWCHARS` causes string character syntax to be parsed in raw strings: `#!py r'\u0040'` --> `#!py r'@'`. This will
341+
handle standard string escapes and Unicode including `#!py r'\N{CHAR NAME}'`.
272342

273343
#### `fnmatch.NEGATE, fnmatch.N` {: #negate}
274344

0 commit comments

Comments
 (0)