-
Notifications
You must be signed in to change notification settings - Fork 27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
✨ Add ignore patterns #92
Merged
Merged
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
89a4a47
Add glob_to_re function by @godlygeek
lemonyte 436390e
Add option for ignore patterns
lemonyte 8b0cd0e
Exit gracefully when no files are found
lemonyte aa6e4f0
Disable expanding args on windows
lemonyte a968598
Make entrypoint a separate partial
lemonyte b9fbfec
Fix linting errors
lemonyte fa50122
Update bump_pydantic/main.py
lemonyte 01cf883
Move glob functions to helpers module, make glob_to_re more reliable
lemonyte 3ad7649
Add tests for match_glob
lemonyte baf62c7
Fix ruff error in test_glob
lemonyte 98e6d55
Type hint fragments list
lemonyte f9191c5
rebase
Kludex 9726de5
Merge branch 'main' into ignore-patterns
lemonyte 9900f9f
Rename helpers.py to glob_helpers.py
lemonyte f8f418e
Rename test_glob.py to test_glob_helpers.py
lemonyte d43e64b
Add message when all files were filtered
Kludex File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
from bump_pydantic.main import app | ||
from bump_pydantic.main import entrypoint | ||
|
||
if __name__ == "__main__": | ||
app() | ||
entrypoint() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import fnmatch | ||
import re | ||
from pathlib import Path | ||
from typing import List | ||
|
||
MATCH_SEP = r"(?:/|\\)" | ||
MATCH_SEP_OR_END = r"(?:/|\\|\Z)" | ||
MATCH_NON_RECURSIVE = r"[^/\\]*" | ||
MATCH_RECURSIVE = r"(?:.*)" | ||
|
||
|
||
def glob_to_re(pattern: str) -> str: | ||
"""Translate a glob pattern to a regular expression for matching.""" | ||
fragments: List[str] = [] | ||
for segment in re.split(r"/|\\", pattern): | ||
if segment == "": | ||
continue | ||
if segment == "**": | ||
# Remove previous separator match, so the recursive match can match zero or more segments. | ||
if fragments and fragments[-1] == MATCH_SEP: | ||
fragments.pop() | ||
fragments.append(MATCH_RECURSIVE) | ||
elif "**" in segment: | ||
raise ValueError("invalid pattern: '**' can only be an entire path component") | ||
else: | ||
fragment = fnmatch.translate(segment) | ||
fragment = fragment.replace(r"(?s:", r"(?:") | ||
fragment = fragment.replace(r".*", MATCH_NON_RECURSIVE) | ||
fragment = fragment.replace(r"\Z", r"") | ||
fragments.append(fragment) | ||
fragments.append(MATCH_SEP) | ||
# Remove trailing MATCH_SEP, so it can be replaced with MATCH_SEP_OR_END. | ||
if fragments and fragments[-1] == MATCH_SEP: | ||
fragments.pop() | ||
fragments.append(MATCH_SEP_OR_END) | ||
return rf"(?s:{''.join(fragments)})" | ||
|
||
|
||
def match_glob(path: Path, pattern: str) -> bool: | ||
"""Check if a path matches a glob pattern. | ||
|
||
If the pattern ends with a directory separator, the path must be a directory. | ||
""" | ||
match = bool(re.fullmatch(glob_to_re(pattern), str(path))) | ||
if pattern.endswith("/") or pattern.endswith("\\"): | ||
return match and path.is_dir() | ||
return match |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
from __future__ import annotations | ||
|
||
from pathlib import Path | ||
|
||
import pytest | ||
|
||
from bump_pydantic.glob_helpers import glob_to_re, match_glob | ||
|
||
|
||
class TestGlobHelpers: | ||
match_glob_values: list[tuple[str, Path, bool]] = [ | ||
("foo", Path("foo"), True), | ||
("foo", Path("bar"), False), | ||
("foo", Path("foo/bar"), False), | ||
("*", Path("foo"), True), | ||
("*", Path("bar"), True), | ||
("*", Path("foo/bar"), False), | ||
("**", Path("foo"), True), | ||
("**", Path("foo/bar"), True), | ||
("**", Path("foo/bar/baz/qux"), True), | ||
("foo/bar", Path("foo/bar"), True), | ||
("foo/bar", Path("foo"), False), | ||
("foo/bar", Path("far"), False), | ||
("foo/bar", Path("foo/foo"), False), | ||
("foo/*", Path("foo/bar"), True), | ||
("foo/*", Path("foo/bar/baz"), False), | ||
("foo/*", Path("foo"), False), | ||
("foo/*", Path("bar"), False), | ||
("foo/**", Path("foo/bar"), True), | ||
("foo/**", Path("foo/bar/baz"), True), | ||
("foo/**", Path("foo/bar/baz/qux"), True), | ||
("foo/**", Path("foo"), True), | ||
("foo/**", Path("bar"), False), | ||
("foo/**/bar", Path("foo/bar"), True), | ||
("foo/**/bar", Path("foo/baz/bar"), True), | ||
("foo/**/bar", Path("foo/baz/qux/bar"), True), | ||
("foo/**/bar", Path("foo/baz/qux"), False), | ||
("foo/**/bar", Path("foo/bar/baz"), False), | ||
("foo/**/bar", Path("foo/bar/bar"), True), | ||
("foo/**/bar", Path("foo"), False), | ||
("foo/**/bar", Path("bar"), False), | ||
("foo/**/*/bar", Path("foo/bar"), False), | ||
("foo/**/*/bar", Path("foo/baz/bar"), True), | ||
("foo/**/*/bar", Path("foo/baz/qux/bar"), True), | ||
("foo/**/*/bar", Path("foo/baz/qux"), False), | ||
("foo/**/*/bar", Path("foo/bar/baz"), False), | ||
("foo/**/*/bar", Path("foo/bar/bar"), True), | ||
("foo/**/*/bar", Path("foo"), False), | ||
("foo/**/*/bar", Path("bar"), False), | ||
("foo/ba*", Path("foo/bar"), True), | ||
("foo/ba*", Path("foo/baz"), True), | ||
("foo/ba*", Path("foo/qux"), False), | ||
("foo/ba*", Path("foo/baz/qux"), False), | ||
("foo/ba*", Path("foo/bar/baz"), False), | ||
("foo/ba*", Path("foo"), False), | ||
("foo/ba*", Path("bar"), False), | ||
("foo/**/ba*/*/qux", Path("foo/a/b/c/bar/a/qux"), True), | ||
("foo/**/ba*/*/qux", Path("foo/a/b/c/baz/a/qux"), True), | ||
("foo/**/ba*/*/qux", Path("foo/a/bar/a/qux"), True), | ||
("foo/**/ba*/*/qux", Path("foo/baz/a/qux"), True), | ||
("foo/**/ba*/*/qux", Path("foo/baz/qux"), False), | ||
("foo/**/ba*/*/qux", Path("foo/a/b/c/qux/a/qux"), False), | ||
("foo/**/ba*/*/qux", Path("foo"), False), | ||
("foo/**/ba*/*/qux", Path("bar"), False), | ||
] | ||
|
||
@pytest.mark.parametrize(("pattern", "path", "expected"), match_glob_values) | ||
def test_match_glob(self, pattern: str, path: Path, expected: bool): | ||
expr = glob_to_re(pattern) | ||
assert match_glob(path, pattern) == expected, f"path: {path}, pattern: {pattern}, expr: {expr}" |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why can't this be added to the app instead of using this partial?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Neither Typer nor Click seem to have an option to pass this argument during initialization. The only way to pass it is during invocation of the app through the
__call__()
ormain()
methods.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you have a reference for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR pallets/click#1918 added the parameter, only to the
main
method.The only mention of this parameter in Click's documentation is under the
main
method here.pallets/click#1918 (comment) explains how to use it in an entrypoint.