diff options
author | Florian Bruhin <me@the-compiler.org> | 2021-03-11 14:51:47 +0100 |
---|---|---|
committer | Florian Bruhin <me@the-compiler.org> | 2021-03-11 16:45:40 +0100 |
commit | 4709d46936b379605aca110c7dd336d1cff45db6 (patch) | |
tree | 3641c2f36be03c3cf6a549e591cfd3be432f9d36 /qutebrowser/utils | |
parent | dc5a63f70bbdaf342511344e70f45562ecbf5a9e (diff) | |
download | qutebrowser-4709d46936b379605aca110c7dd336d1cff45db6.tar.gz qutebrowser-4709d46936b379605aca110c7dd336d1cff45db6.zip |
Add custom VersionNumber class
Qt's API is kind of painful, and we need some custom functionality
anyways. Wrap QVersionNumber with our own class instead of piling up
workarounds.
Diffstat (limited to 'qutebrowser/utils')
-rw-r--r-- | qutebrowser/utils/qtutils.py | 12 | ||||
-rw-r--r-- | qutebrowser/utils/utils.py | 86 | ||||
-rw-r--r-- | qutebrowser/utils/version.py | 16 |
3 files changed, 76 insertions, 38 deletions
diff --git a/qutebrowser/utils/qtutils.py b/qutebrowser/utils/qtutils.py index f7c5a3ce0..01234a42b 100644 --- a/qutebrowser/utils/qtutils.py +++ b/qutebrowser/utils/qtutils.py @@ -98,15 +98,15 @@ def version_check(version: str, if compiled and exact: raise ValueError("Can't use compiled=True with exact=True!") - parsed = utils.parse_version(version) + parsed = utils.VersionNumber.parse(version) op = operator.eq if exact else operator.ge - result = op(utils.parse_version(qVersion()), parsed) + result = op(utils.VersionNumber.parse(qVersion()), parsed) if compiled and result: # qVersion() ==/>= parsed, now check if QT_VERSION_STR ==/>= parsed. - result = op(utils.parse_version(QT_VERSION_STR), parsed) + result = op(utils.VersionNumber.parse(QT_VERSION_STR), parsed) if compiled and result: # Finally, check PYQT_VERSION_STR as well. - result = op(utils.parse_version(PYQT_VERSION_STR), parsed) + result = op(utils.VersionNumber.parse(PYQT_VERSION_STR), parsed) return result @@ -116,8 +116,8 @@ MAX_WORLD_ID = 256 def is_new_qtwebkit() -> bool: """Check if the given version is a new QtWebKit.""" assert qWebKitVersion is not None - return (utils.parse_version(qWebKitVersion()) > - utils.parse_version('538.1')) + return (utils.VersionNumber.parse(qWebKitVersion()) > + utils.VersionNumber.parse('538.1')) def is_single_process() -> bool: diff --git a/qutebrowser/utils/utils.py b/qutebrowser/utils/utils.py index 698a608ef..af9d5fad7 100644 --- a/qutebrowser/utils/utils.py +++ b/qutebrowser/utils/utils.py @@ -92,26 +92,74 @@ class Comparable(Protocol): ... -if TYPE_CHECKING: - class VersionNumber(Comparable, QVersionNumber): +class VersionNumber: - """WORKAROUND for incorrect PyQt stubs.""" -else: - class VersionNumber(QVersionNumber): + """A representation of a version number.""" - """We can't inherit from Protocol and QVersionNumber at runtime.""" + def __init__(self, *args: int) -> None: + self._ver = QVersionNumber(*args) + if self._ver.isNull(): + raise ValueError("Can't construct a null version") - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - normalized = self.normalized() - if normalized != self: - raise ValueError( - f"Refusing to construct non-normalized version from {args} " - f"(normalized: {tuple(normalized.segments())}).") + normalized = self._ver.normalized() + if normalized != self._ver: + raise ValueError( + f"Refusing to construct non-normalized version from {args} " + f"(normalized: {tuple(normalized.segments())}).") - def __repr__(self): - args = ", ".join(str(s) for s in self.segments()) - return f'VersionNumber({args})' + self.major = self._ver.majorVersion() + self.minor = self._ver.minorVersion() + self.patch = self._ver.microVersion() + self.segments = self._ver.segments() + + assert len(self.segments) <= 3, self.segments + + def __str__(self) -> str: + return ".".join(str(s) for s in self.segments) + + def __repr__(self) -> str: + args = ", ".join(str(s) for s in self.segments) + return f'VersionNumber({args})' + + def strip_patch(self) -> 'VersionNumber': + """Get a new VersionNumber with the patch version removed.""" + return VersionNumber(*self.segments[:2]) + + @classmethod + def parse(cls, s: str) -> 'VersionNumber': + """Parse a version number from a string.""" + ver, _suffix = QVersionNumber.fromString(s) + # FIXME: Should we support a suffix? + + if ver.isNull(): + raise ValueError(f"Failed to parse {s}") + + return cls(*ver.normalized().segments()) + + def __hash__(self) -> int: + return hash(self._ver) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, VersionNumber): + return NotImplemented + return self._ver == other._ver + + def __ne__(self, other: object) -> bool: + if not isinstance(other, VersionNumber): + return NotImplemented + return self._ver != other._ver + + def __ge__(self, other: 'VersionNumber') -> bool: + return self._ver >= other._ver # type: ignore[operator] + + def __gt__(self, other: 'VersionNumber') -> bool: + return self._ver > other._ver # type: ignore[operator] + + def __le__(self, other: 'VersionNumber') -> bool: + return self._ver <= other._ver # type: ignore[operator] + + def __lt__(self, other: 'VersionNumber') -> bool: + return self._ver < other._ver # type: ignore[operator] class Unreachable(Exception): @@ -294,12 +342,6 @@ def read_file_binary(filename: str) -> bytes: return path.read_bytes() -def parse_version(version: str) -> VersionNumber: - """Parse a version string.""" - ver, _suffix = QVersionNumber.fromString(version) - return VersionNumber(ver.normalized()) - - def format_seconds(total_seconds: int) -> str: """Format a count of seconds to get a [H:]M:SS string.""" prefix = '-' if total_seconds < 0 else '' diff --git a/qutebrowser/utils/version.py b/qutebrowser/utils/version.py index a10f50ffc..4a4455c99 100644 --- a/qutebrowser/utils/version.py +++ b/qutebrowser/utils/version.py @@ -160,7 +160,7 @@ def distribution() -> Optional[DistributionInfo]: dist_version: Optional[utils.VersionNumber] = None for version_key in ['VERSION', 'VERSION_ID']: if version_key in info: - dist_version = utils.parse_version(info[version_key]) + dist_version = utils.VersionNumber.parse(info[version_key]) break dist_id = info.get('ID', None) @@ -568,7 +568,7 @@ class WebEngineVersions: } def __str__(self) -> str: - s = f'QtWebEngine {self.webengine.toString()}' + s = f'QtWebEngine {self.webengine}' if self.chromium is not None: s += f', Chromium {self.chromium}' if self.source != 'UA': @@ -585,7 +585,7 @@ class WebEngineVersions: """ assert ua.qt_version is not None, ua return cls( - webengine=utils.parse_version(ua.qt_version), + webengine=utils.VersionNumber.parse(ua.qt_version), chromium=ua.upstream_browser_version, source='UA', ) @@ -602,7 +602,7 @@ class WebEngineVersions: (though hackish) way to get a more accurate result. """ return cls( - webengine=utils.parse_version(versions.webengine), + webengine=utils.VersionNumber.parse(versions.webengine), chromium=versions.chromium, source='ELF', ) @@ -624,11 +624,7 @@ class WebEngineVersions: minor_version = v5_15_3 else: # e.g. 5.14.2 -> 5.14 - segments = pyqt_webengine_version.segments()[:2] - if segments[-1] == 0: - del segments[-1] # pragma: no cover - - minor_version = utils.VersionNumber(*segments) + minor_version = pyqt_webengine_version.strip_patch() return cls._CHROMIUM_VERSIONS.get(minor_version) @@ -651,7 +647,7 @@ class WebEngineVersions: Note that we only can get the PyQtWebEngine version with PyQt 5.13 or newer. With Qt 5.12, we instead rely on qVersion(). """ - parsed = utils.parse_version(pyqt_webengine_version) + parsed = utils.VersionNumber.parse(pyqt_webengine_version) return cls( webengine=parsed, chromium=cls._infer_chromium_version(parsed), |