Skip to content

Commit fce1ab1

Browse files
committed
Merge branch 'zegervdv-case_insensitive_resolver' into devel
2 parents ab93483 + 90abbc7 commit fce1ab1

File tree

2 files changed

+51
-16
lines changed

2 files changed

+51
-16
lines changed

anytree/resolver.py

+40-16
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,17 @@ class Resolver(object):
1212

1313
_match_cache = {}
1414

15-
def __init__(self, pathattr='name'):
16-
"""Resolve :any:`NodeMixin` paths using attribute `pathattr`."""
15+
def __init__(self, pathattr='name', ignorecase=False):
16+
"""
17+
Resolve :any:`NodeMixin` paths using attribute `pathattr`.
18+
19+
Keyword Args:
20+
name (str): Name of the node attribute to be used for resolving
21+
ignorecase (bool): Enable case insensisitve handling.
22+
"""
1723
super(Resolver, self).__init__()
1824
self.pathattr = pathattr
25+
self.ignorecase = ignorecase
1926

2027
def get(self, node, path):
2128
"""
@@ -75,8 +82,19 @@ def get(self, node, path):
7582
7683
.. note:: Please not that :any:`get()` returned `None` in exactly that case above,
7784
which was a bug until version 1.8.1.
85+
86+
Case insensitive matching:
87+
88+
>>> r.get(top, '/TOP')
89+
Traceback (most recent call last):
90+
...
91+
anytree.resolver.ResolverError: unknown root node '/TOP'. root is '/top'.
92+
93+
>>> r = Resolver('name', ignorecase=True)
94+
>>> r.get(top, '/TOp')
95+
Node('/top')
7896
"""
79-
node, parts = self.__start(node, path, Resolver.__cmp)
97+
node, parts = self.__start(node, path, self.__cmp)
8098
for part in parts:
8199
if part == "..":
82100
parent = node.parent
@@ -90,8 +108,9 @@ def get(self, node, path):
90108
return node
91109

92110
def __get(self, node, name):
111+
namestr = str(name)
93112
for child in node.children:
94-
if str(_getattr(child, self.pathattr)) == name:
113+
if self.__cmp(_getattr(child, self.pathattr), namestr):
95114
return child
96115
raise ChildResolverError(node, name, self.pathattr)
97116

@@ -163,7 +182,7 @@ def glob(self, node, path):
163182
...
164183
anytree.resolver.RootResolverError: Cannot go above root node Node('/top')
165184
"""
166-
node, parts = self.__start(node, path, Resolver.__match)
185+
node, parts = self.__start(node, path, self.__match)
167186
return self.__glob(node, parts)
168187

169188
def __start(self, node, path, cmp_):
@@ -172,7 +191,7 @@ def __start(self, node, path, cmp_):
172191
# resolve root
173192
if path.startswith(sep):
174193
node = node.root
175-
rootpart = str(_getattr(node, self.pathattr))
194+
rootpart = _getattr(node, self.pathattr)
176195
parts.pop(0)
177196
if not parts[0]:
178197
msg = "root node missing. root is '%s%s'."
@@ -209,9 +228,9 @@ def __glob(self, node, parts):
209228
def __find(self, node, pat, remainder):
210229
matches = []
211230
for child in node.children:
212-
name = str(_getattr(child, self.pathattr))
231+
name = _getattr(child, self.pathattr)
213232
try:
214-
if Resolver.__match(name, pat):
233+
if self.__match(name, pat):
215234
if remainder:
216235
matches += self.__glob(child, remainder)
217236
else:
@@ -226,20 +245,25 @@ def is_wildcard(path):
226245
"""Return `True` is a wildcard."""
227246
return "?" in path or "*" in path
228247

229-
@staticmethod
230-
def __match(name, pat):
248+
def __match(self, name, pat):
249+
k = (pat, self.ignorecase)
231250
try:
232-
re_pat = Resolver._match_cache[pat]
251+
re_pat = Resolver._match_cache[k]
233252
except KeyError:
234253
res = Resolver.__translate(pat)
235254
if len(Resolver._match_cache) >= _MAXCACHE:
236255
Resolver._match_cache.clear()
237-
Resolver._match_cache[pat] = re_pat = re.compile(res)
256+
flags = 0
257+
if self.ignorecase:
258+
flags |= re.IGNORECASE
259+
Resolver._match_cache[k] = re_pat = re.compile(res, flags=flags)
238260
return re_pat.match(name) is not None
239261

240-
@staticmethod
241-
def __cmp(name, pat):
242-
return name == pat
262+
def __cmp(self, name, pat):
263+
if self.ignorecase:
264+
return name.upper() == pat.upper()
265+
else:
266+
return name == pat
243267

244268
@staticmethod
245269
def __translate(pat):
@@ -283,4 +307,4 @@ def __init__(self, node, child, pathattr):
283307

284308

285309
def _getattr(node, name):
286-
return getattr(node, name, None)
310+
return str(getattr(node, name, None))

tests/test_resolver.py

+11
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,17 @@ def test_same_name():
109109
eq_(r.glob(root, "sub"), [sub0, sub1])
110110

111111

112+
def test_ignorecase():
113+
""" Case insensitive resolver """
114+
root = at.Node("root")
115+
sub0 = at.Node("sub0", parent=root)
116+
sub1 = at.Node("sub1", parent=root)
117+
r = at.Resolver(ignorecase=True)
118+
119+
eq_(r.get(root, "SUB0"), sub0)
120+
eq_(r.glob(root, "SU*1"), [sub1])
121+
122+
112123
def test_enum():
113124
class Animals(IntEnum):
114125
Mammal = 1

0 commit comments

Comments
 (0)