@@ -105,6 +105,7 @@ def _validate_multi_segment_value(val):
105105 return leftover_segments > 0
106106
107107
108+ @functools .lru_cache (maxsize = 1024 )
108109def _build_capture_pattern (template_str ):
109110 """Build a regex pattern to capture wildcard matches from a template.
110111
@@ -159,7 +160,8 @@ def replacer(match):
159160 parts .append (re .escape (literal ))
160161
161162 pattern = "" .join (parts )
162- return pattern , wildcard_types
163+ # Convert wildcard_types to a tuple to ensure the return value is fully hashable and robust
164+ return pattern , tuple (wildcard_types )
163165
164166
165167def _validate_value_against_template (val , template_str , property_name = None ):
@@ -183,12 +185,20 @@ def _validate_value_against_template(val, template_str, property_name=None):
183185 ValueError: If a wildcard within the value violates path traversal rules.
184186 """
185187 if template_str is None or template_str == "*" :
186- pattern , wildcard_types = r"^([^/]+)$" , ["*" ]
187- elif template_str == "**" :
188- pattern , wildcard_types = r"^(.+)$" , ["**" ]
189- else :
190- pattern_body , wildcard_types = _build_capture_pattern (template_str )
191- pattern = "^" + pattern_body + "$"
188+ if val in ("." , ".." ):
189+ name_str = property_name if property_name else "positional variable"
190+ raise ValueError ("Invalid value {} for {}." .format (val , name_str ))
191+ return
192+
193+ if template_str == "**" :
194+ if not _validate_multi_segment_value (val ):
195+ name_str = property_name if property_name else "positional variable"
196+ raise ValueError ("Invalid value {} for {}." .format (val , name_str ))
197+ return
198+
199+ # Sub-template case: use cached capture pattern and regex match
200+ pattern_body , wildcard_types = _build_capture_pattern (template_str )
201+ pattern = "^" + pattern_body + "$"
192202
193203 m = re .match (pattern , val )
194204 if m is not None :
0 commit comments