diff --git a/src/allotropy/parsers/utils/json.py b/src/allotropy/parsers/utils/json.py index 8f7a65483..b4e87d880 100644 --- a/src/allotropy/parsers/utils/json.py +++ b/src/allotropy/parsers/utils/json.py @@ -73,6 +73,14 @@ def _get_custom_key(self, key: str) -> float | str | None: return float_value return self.get(str, key) + def _key_matches(self, match_key: str, key: str) -> bool: + if key == match_key: + return True + # "++" can cause re.compile to fail. Since it is never a valid regex expression + # it is safe to escape it to prevent the error. + match_key = match_key.replace("++", r"\+\+") + return bool(re.fullmatch(match_key, key)) + def _get_matching_keys(self, key_or_keys: str | set[str]) -> set[str]: return { matched @@ -80,9 +88,7 @@ def _get_matching_keys(self, key_or_keys: str | set[str]) -> set[str]: key_or_keys if isinstance(key_or_keys, set) else {key_or_keys} ) for matched in [ - k - for k in self.data.keys() - if k == regex_key or re.fullmatch(regex_key, k) + k for k in self.data.keys() if self._key_matches(str(regex_key), str(k)) ] } diff --git a/src/allotropy/parsers/utils/pandas.py b/src/allotropy/parsers/utils/pandas.py index a3dca0225..5b1e47f84 100644 --- a/src/allotropy/parsers/utils/pandas.py +++ b/src/allotropy/parsers/utils/pandas.py @@ -273,6 +273,14 @@ def _get_custom_key(self, key: str) -> float | str | None: return float_value return self.get(str, key) + def _key_matches(self, match_key: str, key: str) -> bool: + if key == match_key: + return True + # "++" can cause re.compile to fail. Since it is never a valid regex expression + # it is safe to escape it to prevent the error. + match_key = match_key.replace("++", r"\+\+") + return bool(re.fullmatch(match_key, key)) + def _get_matching_keys(self, key_or_keys: str | set[str]) -> set[str]: return { matched @@ -282,7 +290,7 @@ def _get_matching_keys(self, key_or_keys: str | set[str]) -> set[str]: for matched in [ k for k in self.series.index - if k == regex_key or re.fullmatch(str(regex_key), str(k)) + if self._key_matches(str(regex_key), str(k)) ] } diff --git a/src/allotropy/parsers/utils/strict_xml_element.py b/src/allotropy/parsers/utils/strict_xml_element.py index 934c33d40..10f73d1a8 100644 --- a/src/allotropy/parsers/utils/strict_xml_element.py +++ b/src/allotropy/parsers/utils/strict_xml_element.py @@ -137,6 +137,14 @@ def _get_all_available_keys(self) -> set[str]: return keys + def _key_matches(self, match_key: str, key: str) -> bool: + if key == match_key: + return True + # "++" can cause re.compile to fail. Since it is never a valid regex expression + # it is safe to escape it to prevent the error. + match_key = match_key.replace("++", r"\+\+") + return bool(re.fullmatch(match_key, key)) + def _get_matching_keys(self, key_or_keys: str | set[str]) -> set[str]: """Get keys that match the given pattern(s).""" all_keys = self._get_all_available_keys() @@ -155,9 +163,7 @@ def _get_matching_keys(self, key_or_keys: str | set[str]) -> set[str]: matches_found = False # Check for exact match - if any( - k for k in all_keys if k == attr_key or re.fullmatch(attr_key, k) - ): + if any(k for k in all_keys if self._key_matches(attr_key, k)): normalized_keys.add(attr_key) matches_found = True @@ -184,9 +190,7 @@ def _get_matching_keys(self, key_or_keys: str | set[str]) -> set[str]: return { matched for regex_key in normalized_keys - for matched in [ - k for k in all_keys if k == regex_key or re.fullmatch(regex_key, k) - ] + for matched in [k for k in all_keys if self._key_matches(str(regex_key), k)] } def mark_read(self, key_or_keys: str | set[str]) -> None: @@ -266,7 +270,7 @@ def _apply_regex_filter( ) -> set[str]: """Apply regex filter to attribute keys if provided.""" if regex: - return {k for k in attribute_keys if re.fullmatch(regex, k)} + return {k for k in attribute_keys if self._key_matches(regex, k)} return attribute_keys def _process_attr_key(