From ad01dd414f03ab7920a10b14d8bcd3aeb762c28c Mon Sep 17 00:00:00 2001 From: masklinn Date: Sat, 13 Jul 2024 13:55:03 +0200 Subject: [PATCH] Rename the string regex attribute of matchers to `regex` It's kinda dumb that I didn't do that in the first place and I'm not entirely sure why I missed it... Anyway there is no reason to make `pattern` be the string pattern and `regex` be the compiled pattern, that unnecessarily diverges from the regexes.yaml naming for the corresponding attribute which is a shame, and the compiled regex in python is... `re.Pattern`, so it makes more sense on both axis to have `pattern: re.Pattern[str]` and `regex: str`. Also add a `regex_flag` attribute/property on the device matchers for the string version of the flags. --- src/ua_parser/core.py | 2 +- src/ua_parser/lazy.py | 46 +++++++++++++++++++++------------------ src/ua_parser/matchers.py | 44 +++++++++++++++++++++---------------- src/ua_parser/re2.py | 8 +++---- 4 files changed, 55 insertions(+), 45 deletions(-) diff --git a/src/ua_parser/core.py b/src/ua_parser/core.py index 5a1bea4..8ea880d 100644 --- a/src/ua_parser/core.py +++ b/src/ua_parser/core.py @@ -240,7 +240,7 @@ def __call__(self, ua: str) -> Optional[T]: @property @abc.abstractmethod - def pattern(self) -> str: + def regex(self) -> str: """Returns the matcher's pattern.""" ... diff --git a/src/ua_parser/lazy.py b/src/ua_parser/lazy.py index bf0ff7f..c5aa5e2 100644 --- a/src/ua_parser/lazy.py +++ b/src/ua_parser/lazy.py @@ -17,7 +17,7 @@ class UserAgentMatcher(Matcher[UserAgent]): """ - pattern: str = "" + regex: str = "" family: str major: Optional[str] minor: Optional[str] @@ -33,7 +33,7 @@ def __init__( patch: Optional[str] = None, patch_minor: Optional[str] = None, ) -> None: - self.pattern = regex + self.regex = regex self.family = family or "$1" self.major = major self.minor = minor @@ -41,7 +41,7 @@ def __init__( self.patch_minor = patch_minor def __call__(self, ua: str) -> Optional[UserAgent]: - if m := self.regex.search(ua): + if m := self.pattern.search(ua): return UserAgent( family=( self.family.replace("$1", m[1]) @@ -56,8 +56,8 @@ def __call__(self, ua: str) -> Optional[UserAgent]: return None @cached_property - def regex(self) -> Pattern[str]: - return re.compile(self.pattern) + def pattern(self) -> Pattern[str]: + return re.compile(self.regex) def __repr__(self) -> str: fields = [ @@ -69,7 +69,7 @@ def __repr__(self) -> str: ] args = "".join(f", {k}={v!r}" for k, v in fields if v is not None) - return f"UserAgentMatcher({self.pattern!r}{args})" + return f"UserAgentMatcher({self.regex!r}{args})" class OSMatcher(Matcher[OS]): @@ -81,7 +81,7 @@ class OSMatcher(Matcher[OS]): """ - pattern: str = "" + regex: str = "" family: str major: str minor: str @@ -97,7 +97,7 @@ def __init__( patch: Optional[str] = None, patch_minor: Optional[str] = None, ) -> None: - self.pattern = regex + self.regex = regex self.family = family or "$1" self.major = major or "$2" self.minor = minor or "$3" @@ -105,7 +105,7 @@ def __init__( self.patch_minor = patch_minor or "$5" def __call__(self, ua: str) -> Optional[OS]: - if m := self.regex.search(ua): + if m := self.pattern.search(ua): family = replacer(self.family, m) if family is None: raise ValueError(f"Unable to find OS family in {ua}") @@ -119,8 +119,8 @@ def __call__(self, ua: str) -> Optional[OS]: return None @cached_property - def regex(self) -> Pattern[str]: - return re.compile(self.pattern) + def pattern(self) -> Pattern[str]: + return re.compile(self.regex) def __repr__(self) -> str: fields = [ @@ -132,7 +132,7 @@ def __repr__(self) -> str: ] args = "".join(f", {k}={v!r}" for k, v in fields if v is not None) - return f"OSMatcher({self.pattern!r}{args})" + return f"OSMatcher({self.regex!r}{args})" class DeviceMatcher(Matcher[Device]): @@ -144,8 +144,8 @@ class DeviceMatcher(Matcher[Device]): """ - pattern: str = "" - flags: int = 0 + regex: str = "" + regex_flag: Optional[Literal["i"]] = None family: str brand: str model: str @@ -158,14 +158,14 @@ def __init__( brand: Optional[str] = None, model: Optional[str] = None, ) -> None: - self.pattern = regex - self.flags = re.IGNORECASE if regex_flag == "i" else 0 + self.regex = regex + self.regex_flag = regex_flag self.family = family or "$1" self.brand = brand or "" self.model = model or "$1" def __call__(self, ua: str) -> Optional[Device]: - if m := self.regex.search(ua): + if m := self.pattern.search(ua): family = replacer(self.family, m) if family is None: raise ValueError(f"Unable to find device family in {ua}") @@ -176,9 +176,13 @@ def __call__(self, ua: str) -> Optional[Device]: ) return None + @property + def flags(self) -> int: + return re.IGNORECASE if self.regex_flag == "i" else 0 + @cached_property - def regex(self) -> Pattern[str]: - return re.compile(self.pattern, flags=self.flags) + def pattern(self) -> Pattern[str]: + return re.compile(self.regex, flags=self.flags) def __repr__(self) -> str: fields = [ @@ -186,7 +190,7 @@ def __repr__(self) -> str: ("brand", self.brand or None), ("model", self.model if self.model != "$1" else None), ] - iflag = ', "i"' if self.flags & re.IGNORECASE else "" + iflag = ', "i"' if self.regex_flag else "" args = iflag + "".join(f", {k}={v!r}" for k, v in fields if v is not None) - return f"DeviceMatcher({self.pattern!r}{args})" + return f"DeviceMatcher({self.regex!r}{args})" diff --git a/src/ua_parser/matchers.py b/src/ua_parser/matchers.py index 03cd460..3956b3b 100644 --- a/src/ua_parser/matchers.py +++ b/src/ua_parser/matchers.py @@ -13,7 +13,7 @@ class UserAgentMatcher(Matcher[UserAgent]): """ - regex: Pattern[str] + pattern: Pattern[str] family: str major: Optional[str] minor: Optional[str] @@ -29,7 +29,7 @@ def __init__( patch: Optional[str] = None, patch_minor: Optional[str] = None, ) -> None: - self.regex = re.compile(regex) + self.pattern = re.compile(regex) self.family = family or "$1" self.major = major self.minor = minor @@ -37,7 +37,7 @@ def __init__( self.patch_minor = patch_minor def __call__(self, ua: str) -> Optional[UserAgent]: - if m := self.regex.search(ua): + if m := self.pattern.search(ua): return UserAgent( family=( self.family.replace("$1", m[1]) @@ -52,8 +52,8 @@ def __call__(self, ua: str) -> Optional[UserAgent]: return None @property - def pattern(self) -> str: - return self.regex.pattern + def regex(self) -> str: + return self.pattern.pattern def __repr__(self) -> str: fields = [ @@ -65,7 +65,7 @@ def __repr__(self) -> str: ] args = "".join(f", {k}={v!r}" for k, v in fields if v is not None) - return f"UserAgentMatcher({self.pattern!r}{args})" + return f"UserAgentMatcher({self.regex!r}{args})" class OSMatcher(Matcher[OS]): @@ -74,7 +74,7 @@ class OSMatcher(Matcher[OS]): """ - regex: Pattern[str] + pattern: Pattern[str] family: str major: str minor: str @@ -90,7 +90,7 @@ def __init__( patch: Optional[str] = None, patch_minor: Optional[str] = None, ) -> None: - self.regex = re.compile(regex) + self.pattern = re.compile(regex) self.family = family or "$1" self.major = major or "$2" self.minor = minor or "$3" @@ -98,7 +98,7 @@ def __init__( self.patch_minor = patch_minor or "$5" def __call__(self, ua: str) -> Optional[OS]: - if m := self.regex.search(ua): + if m := self.pattern.search(ua): family = replacer(self.family, m) if family is None: raise ValueError(f"Unable to find OS family in {ua}") @@ -112,8 +112,8 @@ def __call__(self, ua: str) -> Optional[OS]: return None @property - def pattern(self) -> str: - return self.regex.pattern + def regex(self) -> str: + return self.pattern.pattern def __repr__(self) -> str: fields = [ @@ -125,7 +125,7 @@ def __repr__(self) -> str: ] args = "".join(f", {k}={v!r}" for k, v in fields if v is not None) - return f"OSMatcher({self.pattern!r}{args})" + return f"OSMatcher({self.regex!r}{args})" class DeviceMatcher(Matcher[Device]): @@ -134,7 +134,7 @@ class DeviceMatcher(Matcher[Device]): """ - regex: Pattern[str] + pattern: Pattern[str] family: str brand: str model: str @@ -147,13 +147,15 @@ def __init__( brand: Optional[str] = None, model: Optional[str] = None, ) -> None: - self.regex = re.compile(regex, flags=re.IGNORECASE if regex_flag == "i" else 0) + self.pattern = re.compile( + regex, flags=re.IGNORECASE if regex_flag == "i" else 0 + ) self.family = family or "$1" self.brand = brand or "" self.model = model or "$1" def __call__(self, ua: str) -> Optional[Device]: - if m := self.regex.search(ua): + if m := self.pattern.search(ua): family = replacer(self.family, m) if family is None: raise ValueError(f"Unable to find device family in {ua}") @@ -165,12 +167,16 @@ def __call__(self, ua: str) -> Optional[Device]: return None @property - def pattern(self) -> str: - return self.regex.pattern + def regex(self) -> str: + return self.pattern.pattern + + @property + def regex_flag(self) -> str: + return "i" if self.flags & re.IGNORECASE else "" @property def flags(self) -> int: - return self.regex.flags + return self.pattern.flags def __repr__(self) -> str: fields = [ @@ -181,4 +187,4 @@ def __repr__(self) -> str: iflag = ', "i"' if self.flags & re.IGNORECASE else "" args = iflag + "".join(f", {k}={v!r}" for k, v in fields if v is not None) - return f"DeviceMatcher({self.pattern!r}{args})" + return f"DeviceMatcher({self.regex!r}{args})" diff --git a/src/ua_parser/re2.py b/src/ua_parser/re2.py index f3d95f7..83a4a14 100644 --- a/src/ua_parser/re2.py +++ b/src/ua_parser/re2.py @@ -38,7 +38,7 @@ def __init__( if self.user_agent_matchers: self.ua = re2.Filter() for u in self.user_agent_matchers: - self.ua.Add(u.pattern) + self.ua.Add(u.regex) self.ua.Compile() else: self.ua = DummyFilter() @@ -46,7 +46,7 @@ def __init__( if self.os_matchers: self.os = re2.Filter() for o in self.os_matchers: - self.os.Add(o.pattern) + self.os.Add(o.regex) self.os.Compile() else: self.os = DummyFilter() @@ -58,9 +58,9 @@ def __init__( # no pattern uses global flags, but since they're not # supported in JS that seems safe. if d.flags & re.IGNORECASE: - self.devices.Add("(?i)" + d.pattern) + self.devices.Add("(?i)" + d.regex) else: - self.devices.Add(d.pattern) + self.devices.Add(d.regex) self.devices.Compile() else: self.devices = DummyFilter()