New Upstream Release - python-wcmatch
Ready changes
Summary
Merged new upstream version: 8.4.1 (was: 8.4).
Resulting package
Built on 2022-10-20T11:32 (took 2m13s)
The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:
apt install -t fresh-releases python3-wcmatch
Lintian Result
Diff
diff --git a/debian/changelog b/debian/changelog
index b45c50e..30252af 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+python-wcmatch (8.4.1-1) UNRELEASED; urgency=low
+
+ * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk> Thu, 20 Oct 2022 11:30:54 -0000
+
python-wcmatch (8.4-2) unstable; urgency=medium
[ Debian Janitor ]
diff --git a/docs/src/markdown/about/changelog.md b/docs/src/markdown/about/changelog.md
index df2082d..2ef5859 100644
--- a/docs/src/markdown/about/changelog.md
+++ b/docs/src/markdown/about/changelog.md
@@ -1,5 +1,10 @@
# Changelog
+## 8.4.1
+
+- **FIX**: Windows drive path separators should normalize like other path separators.
+- **FIX**: Fix a Windows pattern parsing issue that caused absolute paths with ambiguous drives to not parse correctly.
+
## 8.4
- **NEW**: Drop support for Python 3.6.
diff --git a/docs/src/markdown/fnmatch.md b/docs/src/markdown/fnmatch.md
index 576da39..a68901a 100644
--- a/docs/src/markdown/fnmatch.md
+++ b/docs/src/markdown/fnmatch.md
@@ -22,7 +22,7 @@ Pattern | Meaning
`[seq]` | Matches any character in seq.
`[!seq]` | Matches any character not in seq. Will also accept character exclusions in the form of `[^seq]`.
`[[:alnum:]]` | POSIX style character classes inside sequences. See [POSIX Character Classes](#posix-character-classes) for more info.
-`\` | Escapes characters. If applied to a meta character, it will be treated as a normal character.
+`\` | Escapes characters. If applied to a meta character or non-meta characters, the character will be treated as a literal character. If applied to another escape, the backslash will be a literal backslash.
`!` | When used at the start of a pattern, the pattern will be an exclusion pattern. Requires the [`NEGATE`](#negate) flag. If also using the [`MINUSNEGATE`](#minusnegate) flag, `-` will be used instead of `!`.
`?(pattern_list)` | The pattern matches if zero or one occurrences of any of the patterns in the `pattern_list` match the input string. Requires the [`EXTMATCH`](#extmatch) flag.
`*(pattern_list)` | The pattern matches if zero or more occurrences of any of the patterns in the `pattern_list` match the input string. Requires the [`EXTMATCH`](#extmatch) flag.
@@ -63,7 +63,7 @@ a list of patterns. It will return a boolean indicating whether the file name wa
```pycon3
>>> from wcmatch import fnmatch
->>> fnmatch.fnmatch('test.txt', r'@(*.txt|*.py)', flags=fnmatch.EXTMATCH)
+>>> fnmatch.fnmatch('test.txt', '@(*.txt|*.py)', flags=fnmatch.EXTMATCH)
True
```
@@ -71,7 +71,7 @@ When applying multiple patterns, a file matches if it matches any of the pattern
```pycon3
>>> from wcmatch import fnmatch
->>> fnmatch.fnmatch('test.txt', [r'*.txt', r'*.py'], flags=fnmatch.EXTMATCH)
+>>> fnmatch.fnmatch('test.txt', ['*.txt', '*.py'], flags=fnmatch.EXTMATCH)
True
```
@@ -93,13 +93,13 @@ meant to filter other patterns, not match anything by themselves.
```pycon3
>>> from wcmatch import fnmatch
->>> fnmatch.fnmatch('test.py', r'*|!*.py', flags=fnmatch.NEGATE | fnmatch.SPLIT)
+>>> fnmatch.fnmatch('test.py', '*|!*.py', flags=fnmatch.NEGATE | fnmatch.SPLIT)
False
->>> fnmatch.fnmatch('test.txt', r'*|!*.py', flags=fnmatch.NEGATE | fnmatch.SPLIT)
+>>> fnmatch.fnmatch('test.txt', '*|!*.py', flags=fnmatch.NEGATE | fnmatch.SPLIT)
True
->>> fnmatch.fnmatch('test.txt', [r'*.txt', r'!avoid.txt'], flags=fnmatch.NEGATE)
+>>> fnmatch.fnmatch('test.txt', ['*.txt', '!avoid.txt'], flags=fnmatch.NEGATE)
True
->>> fnmatch.fnmatch('avoid.txt', [r'*.txt', r'!avoid.txt'], flags=fnmatch.NEGATE)
+>>> fnmatch.fnmatch('avoid.txt', ['*.txt', '!avoid.txt'], flags=fnmatch.NEGATE)
False
```
@@ -110,9 +110,9 @@ pattern were given: `*` and `!*.md`.
```pycon3
>>> from wcmatch import fnmatch
->>> fnmatch.fnmatch('test.py', r'!*.py', flags=fnmatch.NEGATE | fnmatch.NEGATEALL)
+>>> fnmatch.fnmatch('test.py', '!*.py', flags=fnmatch.NEGATE | fnmatch.NEGATEALL)
False
->>> fnmatch.fnmatch('test.txt', r'!*.py', flags=fnmatch.NEGATE | fnmatch.NEGATEALL)
+>>> fnmatch.fnmatch('test.txt', '!*.py', flags=fnmatch.NEGATE | fnmatch.NEGATEALL)
True
```
@@ -135,7 +135,7 @@ pattern or a list of patterns.It returns a list of all files that matched the pa
```pycon3
>>> from wcmatch import fnmatch
->>> fnmatch.filter(['a.txt', 'b.txt', 'c.py'], r'*.txt')
+>>> fnmatch.filter(['a.txt', 'b.txt', 'c.py'], '*.txt')
['a.txt', 'b.txt']
```
@@ -159,9 +159,9 @@ matches at least one inclusion pattern and matches **none** of the exclusion pat
```pycon3
>>> from wcmatch import fnmatch
->>> fnmatch.translate(r'*.{a,{b,c}}', flags=fnmatch.BRACE)
+>>> fnmatch.translate('*.{a,{b,c}}', flags=fnmatch.BRACE)
(['^(?s:(?=.)(?![.]).*?\\.a)$', '^(?s:(?=.)(?![.]).*?\\.b)$', '^(?s:(?=.)(?![.]).*?\\.c)$'], [])
->>> fnmatch.translate(r'**|!*.{a,{b,c}}', flags=fnmatch.BRACE | fnmatch.NEGATE | fnmatch.SPLIT)
+>>> fnmatch.translate('**|!*.{a,{b,c}}', flags=fnmatch.BRACE | fnmatch.NEGATE | fnmatch.SPLIT)
(['^(?s:(?=.)(?![.]).*?)$'], ['^(?s:(?=.).*?\\.a)$', '^(?s:(?=.).*?\\.b)$', '^(?s:(?=.).*?\\.c)$'])
```
@@ -346,9 +346,9 @@ it's warnings related to pairing it with `SPLIT`.
```pycon3
>>> from wcmatch import fnmatch
->>> fnmatch.fnmatch('test.txt', r'*.txt|*.py', flags=fnmatch.SPLIT)
+>>> fnmatch.fnmatch('test.txt', '*.txt|*.py', flags=fnmatch.SPLIT)
True
->>> fnmatch.fnmatch('test.py', r'*.txt|*.py', flags=fnmatch.SPLIT)
+>>> fnmatch.fnmatch('test.py', '*.txt|*.py', flags=fnmatch.SPLIT)
True
```
diff --git a/docs/src/markdown/glob.md b/docs/src/markdown/glob.md
index d324acf..cd088d2 100644
--- a/docs/src/markdown/glob.md
+++ b/docs/src/markdown/glob.md
@@ -23,7 +23,7 @@ Pattern | Meaning
`[seq]` | Matches any character in seq.
`[!seq]` | Matches any character not in seq. Will also accept character exclusions in the form of `[^seq]`.
`[[:alnum:]]` | POSIX style character classes inside sequences. See [POSIX Character Classes](#posix-character-classes) for more info.
-`\` | Escapes characters. If applied to a meta character, it will be treated as a normal character.
+`\` | Escapes characters. If applied to a meta character or non-meta characters, the character will be treated as a literal character. If applied to another escape, the backslash will be a literal backslash.
`!` | When used at the start of a pattern, the pattern will be an exclusion pattern. Requires the [`NEGATE`](#negate) flag. If also using the [`MINUSNEGATE`](#minusnegate) flag, `-` will be used instead of `!`.
`?(pattern_list)` | The pattern matches if zero or one occurrences of any of the patterns in the `pattern_list` match the input string. Requires the [`EXTGLOB`](#extglob) flag.
`*(pattern_list)` | The pattern matches if zero or more occurrences of any of the patterns in the `pattern_list` match the input string. Requires the [`EXTGLOB`](#extglob) flag.
@@ -152,6 +152,24 @@ Pattern | Meaning
--8<-- "posix.md"
+## Windows Separators
+
+On Windows, it is not required to use backslashes for path separators as `/` will match path separators for all systems.
+The following will work on Windows and Linux/Unix systems.
+
+```python
+glob.glob('docs/.*')
+```
+
+With that said, you can match Windows separators with backslashes as well. Keep in mind that Wildcard Match allows
+escaped characters in patterns, so to match a literal backslash separator, you must escape the backslash. It is advised
+to use raw strings when using backslashes to make the patterns more readable, but either of the below will work.
+
+```python
+glob.glob(r'docs\\.*')
+glob.glob('docs\\\\.*')
+```
+
## Multi-Pattern Limits
Many of the API functions allow passing in multiple patterns or using either [`BRACE`](#brace) or
@@ -180,7 +198,7 @@ file system returning matching files.
```pycon3
>>> from wcmatch import glob
->>> glob.glob(r'**/*.md')
+>>> glob.glob('**/*.md')
['docs/src/markdown/_snippets/abbr.md', 'docs/src/markdown/_snippets/links.md', 'docs/src/markdown/_snippets/refs.md', 'docs/src/markdown/changelog.md', 'docs/src/markdown/fnmatch.md', 'docs/src/markdown/glob.md', 'docs/src/markdown/index.md', 'docs/src/markdown/installation.md', 'docs/src/markdown/license.md', 'README.md']
```
@@ -188,7 +206,7 @@ Using a list, we can add exclusion patterns and also exclude directories and/or
```pycon3
>>> from wcmatch import glob
->>> glob.glob([r'**/*.md', r'!README.md', r'!**/_snippets'], flags=glob.NEGATE)
+>>> glob.glob(['**/*.md', '!README.md', '!**/_snippets'], flags=glob.NEGATE)
['docs/src/markdown/changelog.md', 'docs/src/markdown/fnmatch.md', 'docs/src/markdown/glob.md', 'docs/src/markdown/index.md', 'docs/src/markdown/installation.md', 'docs/src/markdown/license.md']
```
@@ -196,7 +214,7 @@ When a glob pattern ends with a slash, it will only return directories:
```pycon3
>>> from wcmatch import glob
->>> glob.glob(r'**/')
+>>> glob.glob('**/')
['__pycache__/', 'docs/', 'docs/src/', 'docs/src/markdown/', 'docs/src/markdown/_snippets/', 'docs/theme/', 'requirements/', 'stuff/', 'tests/', 'tests/__pycache__/', 'wcmatch/', 'wcmatch/__pycache__/']
```
@@ -313,7 +331,7 @@ def iglob(patterns, *, flags=0, root_dir=None, dir_fd=None, limit=1000, exclude=
```pycon3
>>> from wcmatch import glob
->>> list(glob.iglob(r'**/*.md'))
+>>> list(glob.iglob('**/*.md'))
['docs/src/markdown/_snippets/abbr.md', 'docs/src/markdown/_snippets/links.md', 'docs/src/markdown/_snippets/refs.md', 'docs/src/markdown/changelog.md', 'docs/src/markdown/fnmatch.md', 'docs/src/markdown/glob.md', 'docs/src/markdown/index.md', 'docs/src/markdown/installation.md', 'docs/src/markdown/license.md', 'README.md']
```
@@ -342,7 +360,7 @@ boolean indicating whether the file path was matched by the pattern(s).
```pycon3
>>> from wcmatch import glob
->>> glob.globmatch('some/path/test.txt', r'**/*/@(*.txt|*.py)', flags=glob.EXTGLOB)
+>>> glob.globmatch('some/path/test.txt', '**/*/@(*.txt|*.py)', flags=glob.EXTGLOB)
True
```
@@ -350,7 +368,7 @@ When applying multiple patterns, a file path matches if it matches any of the pa
```pycon3
>>> from wcmatch import glob
->>> glob.globmatch('some/path/test.txt', [r'**/*/*.txt', r'**/*/*.py'])
+>>> glob.globmatch('some/path/test.txt', ['**/*/*.txt', '**/*/*.py'])
True
```
@@ -361,13 +379,13 @@ to filter other patterns, not match anything by themselves.
```pycon3
>>> from wcmatch import glob
->>> glob.globmatch('some/path/test.py', r'**|!**/*.txt', flags=glob.NEGATE | glob.GLOBSTAR | glob.SPLIT)
+>>> glob.globmatch('some/path/test.py', '**|!**/*.txt', flags=glob.NEGATE | glob.GLOBSTAR | glob.SPLIT)
True
->>> glob.globmatch('some/path/test.txt', r'**|!**/*.txt', flags=glob.NEGATE | glob.GLOBSTAR | glob.SPLIT)
+>>> glob.globmatch('some/path/test.txt', '**|!**/*.txt', flags=glob.NEGATE | glob.GLOBSTAR | glob.SPLIT)
False
->>> glob.globmatch('some/path/test.txt', [r'*/*/*.txt', r'!*/*/avoid.txt'], flags=glob.NEGATE)
+>>> glob.globmatch('some/path/test.txt', ['*/*/*.txt', '!*/*/avoid.txt'], flags=glob.NEGATE)
True
->>> glob.globmatch('some/path/avoid.txt', [r'*/*/*.txt', r'!*/*/avoid.txt'], flags=glob.NEGATE)
+>>> glob.globmatch('some/path/avoid.txt', ['*/*/*.txt', '!*/*/avoid.txt'], flags=glob.NEGATE)
False
```
@@ -379,9 +397,9 @@ if [`GLOBSTAR`](#globstar) was enabled).
```pycon3
>>> from wcmatch import glob
->>> glob.globmatch('some/path/test.py', r'!**/*.txt', flags=glob.NEGATE | glob.GLOBSTAR | glob.NEGATEALL)
+>>> glob.globmatch('some/path/test.py', '!**/*.txt', flags=glob.NEGATE | glob.GLOBSTAR | glob.NEGATEALL)
True
->>> glob.globmatch('some/path/test.txt', r'!**/*.txt', flags=glob.NEGATE | glob.GLOBSTAR | glob.NEGATEALL)
+>>> glob.globmatch('some/path/test.txt', '!**/*.txt', flags=glob.NEGATE | glob.GLOBSTAR | glob.NEGATEALL)
False
```
@@ -485,7 +503,7 @@ for [`globmatch`](#globmatch) is used for `globfilter`, albeit more efficient fo
```pycon3
>>> from wcmatch import glob
->>> glob.globfilter(['some/path/a.txt', 'b.txt', 'another/path/c.py'], r'**/*.txt')
+>>> glob.globfilter(['some/path/a.txt', 'b.txt', 'another/path/c.py'], '**/*.txt')
['some/path/a.txt', 'b.txt']
```
@@ -521,9 +539,9 @@ matches at least one inclusion pattern and matches **none** of the exclusion pat
```pycon3
>>> from wcmatch import glob
->>> glob.translate(r'**/*.{py,txt}')
+>>> glob.translate('**/*.{py,txt}')
(['^(?s:(?=[^/])(?!(?:\\.{1,2})(?:$|[/]))(?:(?!\\.)[^/]*?)?[/]+(?=[^/])(?!(?:\\.{1,2})(?:$|[/]))(?:(?!\\.)[^/]*?)?\\.\\{py,txt\\}[/]*?)$'], [])
->>> glob.translate(r'**|!**/*.{py,txt}', flags=glob.NEGATE | glob.SPLIT)
+>>> glob.translate('**|!**/*.{py,txt}', flags=glob.NEGATE | glob.SPLIT)
(['^(?s:(?=[^/])(?!(?:\\.{1,2})(?:$|[/]))(?:(?!\\.)[^/]*?)?[/]*?)$'], ['^(?s:(?=[^/])(?!(?:\\.{1,2})(?:$|[/]))[^/]*?[/]+(?=[^/])(?!(?:\\.{1,2})(?:$|[/]))[^/]*?\\.\\{py,txt\\}[/]*?)$'])
```
@@ -967,9 +985,9 @@ related to pairing it with `SPLIT`.
```pycon3
>>> from wcmatch import glob
->>> glob.globmatch('test.txt', r'*.txt|*.py', flags=fnmatch.SPLIT)
+>>> glob.globmatch('test.txt', '*.txt|*.py', flags=fnmatch.SPLIT)
True
->>> glob.globmatch('test.py', r'*.txt|*.py', flags=fnmatch.SPLIT)
+>>> glob.globmatch('test.py', '*.txt|*.py', flags=fnmatch.SPLIT)
True
```
diff --git a/mkdocs.yml b/mkdocs.yml
index bee28cc..6b05815 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -109,7 +109,8 @@ extra:
plugins:
- search
- - git-revision-date-localized
+ - git-revision-date-localized:
+ fallback_to_build_date: true
- mkdocs_pymdownx_material_extras
- minify:
minify_html: true
diff --git a/tests/test_glob.py b/tests/test_glob.py
index 9d626be..57331f2 100644
--- a/tests/test_glob.py
+++ b/tests/test_glob.py
@@ -1591,13 +1591,29 @@ class TestSymlinkLoopGlob(unittest.TestCase):
class TestGlobPaths(unittest.TestCase):
"""Test `glob` paths."""
- def test_root(self):
+ @unittest.skipUnless(not sys.platform.startswith('win'), "Linux/Unix specific test")
+ def test_root_unix(self):
"""Test that `glob` translates the root properly."""
- # On Windows, this should translate to the current drive.
# On Linux/Unix, this should translate to the root.
# Basically, we should not return an empty set.
- self.assertTrue(len(glob.glob('/*')) > 0)
+ results = glob.glob('/*')
+ self.assertTrue(len(results) > 0)
+ self.assertTrue('/' not in results)
+
+ @unittest.skipUnless(sys.platform.startswith('win'), "Windows specific test")
+ def test_root_win(self):
+ """Test that `glob` translates the root properly."""
+
+ # On Windows, this should translate to the current drive.
+ # Basically, we should not return an empty set.
+ results = glob.glob('/*')
+ self.assertTrue(len(results) > 0)
+ self.assertTrue('\\' not in results)
+
+ results = glob.glob(r'\\*')
+ self.assertTrue(len(results) > 0)
+ self.assertTrue('\\' not in results)
def test_start(self):
"""Test that starting directory/files are handled properly."""
diff --git a/wcmatch/__meta__.py b/wcmatch/__meta__.py
index 939221b..c273b6e 100644
--- a/wcmatch/__meta__.py
+++ b/wcmatch/__meta__.py
@@ -193,5 +193,5 @@ def parse_version(ver: str) -> Version:
return Version(major, minor, micro, release, pre, post, dev)
-__version_info__ = Version(8, 4, 0, "final")
+__version_info__ = Version(8, 4, 1, "final")
__version__ = __version_info__._get_canonical()
diff --git a/wcmatch/_wcparse.py b/wcmatch/_wcparse.py
index 08aea80..59d6d60 100644
--- a/wcmatch/_wcparse.py
+++ b/wcmatch/_wcparse.py
@@ -362,9 +362,9 @@ def _get_win_drive(
end = m.end(0)
if m.group(3) and RE_WIN_DRIVE_LETTER.match(m.group(0)):
if regex:
- drive = escape_drive(RE_WIN_DRIVE_UNESCAPE.sub(r'\1', m.group(3)), case_sensitive)
+ drive = escape_drive(RE_WIN_DRIVE_UNESCAPE.sub(r'\1', m.group(3)).replace('/', '\\'), case_sensitive)
else:
- drive = RE_WIN_DRIVE_UNESCAPE.sub(r'\1', m.group(0))
+ drive = RE_WIN_DRIVE_UNESCAPE.sub(r'\1', m.group(0)).replace('/', '\\')
slash = bool(m.group(4))
root_specified = True
elif m.group(2):
diff --git a/wcmatch/glob.py b/wcmatch/glob.py
index a8249d6..4560e72 100644
--- a/wcmatch/glob.py
+++ b/wcmatch/glob.py
@@ -314,8 +314,12 @@ class _GlobSplit(Generic[AnyStr]):
i.advance(start)
elif drive is None and root_specified:
parts.append(_GlobPart(b'\\' if is_bytes else '\\', False, False, True, True))
- start = 1
- i.advance(2)
+ if pattern.startswith('/'):
+ start = 0
+ i.advance(1)
+ else:
+ start = 1
+ i.advance(2)
elif not self.win_drive_detect and pattern.startswith('/'):
parts.append(_GlobPart(b'/' if is_bytes else '/', False, False, True, True))
start = 0
Debdiff
[The following lists of changes regard files as different if they have different names, permissions or owners.]
Files in second set of .debs but not in first
-rw-r--r-- root/root /usr/lib/python3/dist-packages/wcmatch-8.4.1.dist-info/METADATA -rw-r--r-- root/root /usr/lib/python3/dist-packages/wcmatch-8.4.1.dist-info/RECORD -rw-r--r-- root/root /usr/lib/python3/dist-packages/wcmatch-8.4.1.dist-info/WHEEL
Files in first set of .debs but not in second
-rw-r--r-- root/root /usr/lib/python3/dist-packages/wcmatch-8.4.dist-info/METADATA -rw-r--r-- root/root /usr/lib/python3/dist-packages/wcmatch-8.4.dist-info/RECORD -rw-r--r-- root/root /usr/lib/python3/dist-packages/wcmatch-8.4.dist-info/WHEEL
No differences were encountered in the control files