Skip to content

Commit 98021e9

Browse files
committed
Refactor and more docu
1 parent 7e6f219 commit 98021e9

1 file changed

Lines changed: 47 additions & 28 deletions

File tree

basic_features/glob_tree.py

Lines changed: 47 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22
import re
33
from collections.abc import Iterable, Sequence
44
from pathlib import PurePath
5-
from typing import cast
5+
from typing import Literal, cast
66

77
import mobase
88

99
from .utils import is_directory
1010

1111
_glob_pattern_matcher = re.compile(r"[*?\[\]]")
1212

13-
PatternPart = str | re.Pattern[str]
13+
PatternPart = str | re.Pattern[str] | Literal["*", "**"]
1414

1515

1616
def glob_tree(
@@ -32,11 +32,11 @@ def glob_tree(
3232
if path and not path.endswith("/"):
3333
path += "/"
3434
if pattern.endswith(("/", "\\")):
35-
for res_path, entry in _glob_tree(file_tree, path, parts):
35+
for res_path, entry in _glob_tree_parts(file_tree, parts, path):
3636
if is_directory(entry):
3737
yield res_path, entry
3838
else:
39-
yield from _glob_tree(file_tree, path, parts)
39+
yield from _glob_tree_parts(file_tree, parts, path)
4040

4141

4242
def parse_glob_pattern(pattern: str) -> list[PatternPart]:
@@ -53,7 +53,7 @@ def parse_glob_pattern(pattern: str) -> list[PatternPart]:
5353
res.append(part)
5454
elif part == "**":
5555
if i + 1 < len(parts) and parts[i + 1] == "**":
56-
raise ValueError("Invalid pattern: '**/**' not supported!")
56+
raise ValueError("Invalid pattern: '**/**' is not supported!")
5757
res.append(part)
5858
elif "**" in part:
5959
raise ValueError(
@@ -72,47 +72,65 @@ def has_wildcards(test_str: str):
7272
return _glob_pattern_matcher.search(test_str)
7373

7474

75-
def _glob_tree(
75+
def _glob_tree_parts(
7676
file_tree: mobase.IFileTree,
77-
path: str,
78-
parts: Sequence[PatternPart],
79-
double_star: bool = False,
77+
pattern_parts: Sequence[PatternPart],
78+
path: str = "",
79+
recursive: bool = False,
8080
) -> Iterable[tuple[str, mobase.FileTreeEntry]]:
81-
# path ends with /
81+
"""Glob the tree with a sequence of pattern parts, consisting of a regex pattern,
82+
`"*"`, `"**"` or a literal file tree entry name, as returned by `parse_glob_pattern`.
83+
84+
Args:
85+
file_tree: `IFileTree` test Args
86+
pattern_parts: List of the path parts: regex, `"*"`, `"**"` or name.
87+
path (optional): Path to the file tree,
88+
**must end with a slash ".../"**. Defaults to "".
89+
recursive (optional): Search for the pattern sequence recursive in the subtree, too.
90+
Same as preceding `pattern_parts` with "**". Defaults to False.
91+
92+
Yields:
93+
(path, entry)
94+
95+
See Also:
96+
parse_glob_pattern
97+
"""
8298
simple_parts = 0
8399
re_pattern: re.Pattern[str] | None = None
84100
doublestar_part = False
85-
for part in parts:
86-
if part == "*":
87-
break
88-
if part == "**":
89-
doublestar_part = True
90-
break
91-
if isinstance(part, re.Pattern):
92-
re_pattern = part
93-
break
94-
simple_parts += 1
101+
for part in pattern_parts:
102+
match part:
103+
case "*":
104+
break
105+
case "**":
106+
doublestar_part = True
107+
break
108+
case re.Pattern():
109+
re_pattern = part
110+
break
111+
case _:
112+
simple_parts += 1
95113
else:
96-
# No glob patterns
97-
sub_path = "/".join(cast(Sequence[str], parts))
114+
# No wildcards
115+
sub_path = "/".join(cast(Sequence[str], pattern_parts))
98116
if (entry := file_tree.find(sub_path)) is not None:
99117
yield path + sub_path, entry
100118
return
101119

102120
if simple_parts:
103121
# Get non pattern part directly
104-
sub_path = "/".join(cast(Sequence[str], parts[:simple_parts]))
122+
sub_path = "/".join(cast(Sequence[str], pattern_parts[:simple_parts]))
105123
entry = file_tree.find(sub_path, mobase.FileTreeEntry.DIRECTORY)
106124
if entry is None or not is_directory(entry):
107125
return
108126
file_tree = entry
109127
path = f"{path}{sub_path}/"
110-
rest = parts[simple_parts + 1 :]
128+
rest = pattern_parts[simple_parts + 1 :]
111129

112130
if doublestar_part:
113131
if rest:
114132
# **/...
115-
yield from _glob_tree(file_tree, path, rest, True)
133+
yield from _glob_tree_parts(file_tree, rest, path, True)
116134
else:
117135
# .../**
118136
yield from all_tree_entries(file_tree, path)
@@ -123,12 +141,13 @@ def _glob_tree(
123141

124142
sub_path = path + name
125143
if not re_pattern or re_pattern.match(name):
144+
# match or "*" part"
126145
if not rest:
127146
yield sub_path, entry
128147
elif is_directory(entry):
129-
yield from _glob_tree(entry, sub_path + "/", rest, double_star)
130-
if double_star and is_directory(entry):
131-
yield from _glob_tree(entry, sub_path + "/", parts, double_star)
148+
yield from _glob_tree_parts(entry, rest, sub_path + "/", recursive)
149+
if recursive and is_directory(entry):
150+
yield from _glob_tree_parts(entry, pattern_parts, sub_path + "/", recursive)
132151

133152

134153
def all_tree_entries(

0 commit comments

Comments
 (0)