misc.python.materialize.mz_version

Version types

  1# Copyright Materialize, Inc. and contributors. All rights reserved.
  2#
  3# Use of this software is governed by the Business Source License
  4# included in the LICENSE file at the root of this repository.
  5#
  6# As of the Change Date specified in that file, in accordance with
  7# the Business Source License, use of this software will be governed
  8# by the Apache License, Version 2.0.
  9
 10"""Version types"""
 11
 12from __future__ import annotations
 13
 14import json
 15import subprocess
 16from typing import TypeVar
 17
 18try:
 19    from semver.version import Version
 20except ImportError:
 21    from semver import VersionInfo as Version  # type: ignore
 22
 23T = TypeVar("T", bound="TypedVersionBase")
 24
 25
 26class TypedVersionBase(Version):
 27    """Typed version, can be parsed from version string"""
 28
 29    @classmethod
 30    def get_prefix(cls) -> str:
 31        raise NotImplementedError(f"Not implemented in {cls}")
 32
 33    @classmethod
 34    def create(
 35        cls: type[T], major: int, minor: int, patch: int, prerelease: str | None = None
 36    ) -> T:
 37        prerelease_suffix = f"-{prerelease}" if prerelease is not None else ""
 38        return cls.parse(
 39            f"{cls.get_prefix()}{major}.{minor}.{patch}{prerelease_suffix}"
 40        )
 41
 42    @classmethod
 43    def parse_without_prefix(
 44        cls: type[T], version_without_prefix: str, drop_dev_suffix: bool = False
 45    ) -> T:
 46        version = f"{cls.get_prefix()}{version_without_prefix}"
 47        return cls.parse(version, drop_dev_suffix=drop_dev_suffix)
 48
 49    @classmethod
 50    def parse(cls: type[T], version: str, drop_dev_suffix: bool = False) -> T:
 51        """Parses a version string with prefix, for example: v0.45.0-dev (f01773cb1) or v0.115.0-dev.0"""
 52        expected_prefix = cls.get_prefix()
 53        if not version.startswith(expected_prefix):
 54            raise ValueError(
 55                f"Invalid version string '{version}', expected prefix '{expected_prefix}'"
 56            )
 57        version = version.removeprefix(expected_prefix)
 58        if " " in version:
 59            version, git_hash = version.split(" ")
 60            if not git_hash[0] == "(" or not git_hash[-1] == ")":
 61                raise ValueError(f"Invalid mz version string: {version}")
 62            # Hash ignored
 63
 64        if drop_dev_suffix:
 65            version, _, _ = version.partition("-dev")
 66
 67        return super().parse(version)
 68
 69    @classmethod
 70    def try_parse(
 71        cls: type[T], version: str, drop_dev_suffix: bool = False
 72    ) -> T | None:
 73        """Parses a version string but returns empty if that fails"""
 74        try:
 75            return cls.parse(version, drop_dev_suffix=drop_dev_suffix)
 76        except ValueError:
 77            return None
 78
 79    @classmethod
 80    def is_valid_version_string(cls, version: str) -> bool:
 81        return cls.try_parse(version) is not None
 82
 83    def str_without_prefix(self) -> str:
 84        return super().__str__()
 85
 86    def __str__(self) -> str:
 87        return f"{self.get_prefix()}{self.str_without_prefix()}"
 88
 89    def is_dev_version(self) -> bool:
 90        return self.prerelease is not None
 91
 92
 93class MzVersion(TypedVersionBase):
 94    """Version of Materialize, can be parsed from version string, SQL, cargo"""
 95
 96    @classmethod
 97    def get_prefix(cls) -> str:
 98        return "v"
 99
100    @classmethod
101    def parse_mz(cls: type[T], version: str, drop_dev_suffix: bool = False) -> T:
102        """Parses a version string with prefix, for example: v0.45.0-dev (f01773cb1) or v0.115.0-dev.0"""
103        return cls.parse(version=version, drop_dev_suffix=drop_dev_suffix)
104
105    @classmethod
106    def parse_cargo(cls) -> MzVersion:
107        """Uses the cargo mz-environmentd package info to get the version of current source code state"""
108        metadata = json.loads(
109            subprocess.check_output(
110                ["cargo", "metadata", "--no-deps", "--format-version=1"]
111            )
112        )
113        for package in metadata["packages"]:
114            if package["name"] == "mz-environmentd":
115                return cls.parse_without_prefix(package["version"])
116        else:
117            raise ValueError("No mz-environmentd version found in cargo metadata")
118
119
120class MzCliVersion(TypedVersionBase):
121    """Version of Materialize APT"""
122
123    @classmethod
124    def get_prefix(cls) -> str:
125        return "mz-v"
126
127
128class MzLspServerVersion(TypedVersionBase):
129    """Version of Materialize LSP Server"""
130
131    @classmethod
132    def get_prefix(cls) -> str:
133        return "mz-lsp-server-v"
134
135
136class MzDebugVersion(TypedVersionBase):
137    """Version of Materialize Debug Tool"""
138
139    @classmethod
140    def get_prefix(cls) -> str:
141        return "mz-debug-v"
class TypedVersionBase(semver.version.Version):
27class TypedVersionBase(Version):
28    """Typed version, can be parsed from version string"""
29
30    @classmethod
31    def get_prefix(cls) -> str:
32        raise NotImplementedError(f"Not implemented in {cls}")
33
34    @classmethod
35    def create(
36        cls: type[T], major: int, minor: int, patch: int, prerelease: str | None = None
37    ) -> T:
38        prerelease_suffix = f"-{prerelease}" if prerelease is not None else ""
39        return cls.parse(
40            f"{cls.get_prefix()}{major}.{minor}.{patch}{prerelease_suffix}"
41        )
42
43    @classmethod
44    def parse_without_prefix(
45        cls: type[T], version_without_prefix: str, drop_dev_suffix: bool = False
46    ) -> T:
47        version = f"{cls.get_prefix()}{version_without_prefix}"
48        return cls.parse(version, drop_dev_suffix=drop_dev_suffix)
49
50    @classmethod
51    def parse(cls: type[T], version: str, drop_dev_suffix: bool = False) -> T:
52        """Parses a version string with prefix, for example: v0.45.0-dev (f01773cb1) or v0.115.0-dev.0"""
53        expected_prefix = cls.get_prefix()
54        if not version.startswith(expected_prefix):
55            raise ValueError(
56                f"Invalid version string '{version}', expected prefix '{expected_prefix}'"
57            )
58        version = version.removeprefix(expected_prefix)
59        if " " in version:
60            version, git_hash = version.split(" ")
61            if not git_hash[0] == "(" or not git_hash[-1] == ")":
62                raise ValueError(f"Invalid mz version string: {version}")
63            # Hash ignored
64
65        if drop_dev_suffix:
66            version, _, _ = version.partition("-dev")
67
68        return super().parse(version)
69
70    @classmethod
71    def try_parse(
72        cls: type[T], version: str, drop_dev_suffix: bool = False
73    ) -> T | None:
74        """Parses a version string but returns empty if that fails"""
75        try:
76            return cls.parse(version, drop_dev_suffix=drop_dev_suffix)
77        except ValueError:
78            return None
79
80    @classmethod
81    def is_valid_version_string(cls, version: str) -> bool:
82        return cls.try_parse(version) is not None
83
84    def str_without_prefix(self) -> str:
85        return super().__str__()
86
87    def __str__(self) -> str:
88        return f"{self.get_prefix()}{self.str_without_prefix()}"
89
90    def is_dev_version(self) -> bool:
91        return self.prerelease is not None

Typed version, can be parsed from version string

@classmethod
def get_prefix(cls) -> str:
30    @classmethod
31    def get_prefix(cls) -> str:
32        raise NotImplementedError(f"Not implemented in {cls}")
@classmethod
def create( cls: type[~T], major: int, minor: int, patch: int, prerelease: str | None = None) -> ~T:
34    @classmethod
35    def create(
36        cls: type[T], major: int, minor: int, patch: int, prerelease: str | None = None
37    ) -> T:
38        prerelease_suffix = f"-{prerelease}" if prerelease is not None else ""
39        return cls.parse(
40            f"{cls.get_prefix()}{major}.{minor}.{patch}{prerelease_suffix}"
41        )
@classmethod
def parse_without_prefix( cls: type[~T], version_without_prefix: str, drop_dev_suffix: bool = False) -> ~T:
43    @classmethod
44    def parse_without_prefix(
45        cls: type[T], version_without_prefix: str, drop_dev_suffix: bool = False
46    ) -> T:
47        version = f"{cls.get_prefix()}{version_without_prefix}"
48        return cls.parse(version, drop_dev_suffix=drop_dev_suffix)
@classmethod
def parse(cls: type[~T], version: str, drop_dev_suffix: bool = False) -> ~T:
50    @classmethod
51    def parse(cls: type[T], version: str, drop_dev_suffix: bool = False) -> T:
52        """Parses a version string with prefix, for example: v0.45.0-dev (f01773cb1) or v0.115.0-dev.0"""
53        expected_prefix = cls.get_prefix()
54        if not version.startswith(expected_prefix):
55            raise ValueError(
56                f"Invalid version string '{version}', expected prefix '{expected_prefix}'"
57            )
58        version = version.removeprefix(expected_prefix)
59        if " " in version:
60            version, git_hash = version.split(" ")
61            if not git_hash[0] == "(" or not git_hash[-1] == ")":
62                raise ValueError(f"Invalid mz version string: {version}")
63            # Hash ignored
64
65        if drop_dev_suffix:
66            version, _, _ = version.partition("-dev")
67
68        return super().parse(version)

Parses a version string with prefix, for example: v0.45.0-dev (f01773cb1) or v0.115.0-dev.0

@classmethod
def try_parse( cls: type[~T], version: str, drop_dev_suffix: bool = False) -> Optional[~T]:
70    @classmethod
71    def try_parse(
72        cls: type[T], version: str, drop_dev_suffix: bool = False
73    ) -> T | None:
74        """Parses a version string but returns empty if that fails"""
75        try:
76            return cls.parse(version, drop_dev_suffix=drop_dev_suffix)
77        except ValueError:
78            return None

Parses a version string but returns empty if that fails

@classmethod
def is_valid_version_string(cls, version: str) -> bool:
80    @classmethod
81    def is_valid_version_string(cls, version: str) -> bool:
82        return cls.try_parse(version) is not None
def str_without_prefix(self) -> str:
84    def str_without_prefix(self) -> str:
85        return super().__str__()
def is_dev_version(self) -> bool:
90    def is_dev_version(self) -> bool:
91        return self.prerelease is not None
class MzVersion(TypedVersionBase):
 94class MzVersion(TypedVersionBase):
 95    """Version of Materialize, can be parsed from version string, SQL, cargo"""
 96
 97    @classmethod
 98    def get_prefix(cls) -> str:
 99        return "v"
100
101    @classmethod
102    def parse_mz(cls: type[T], version: str, drop_dev_suffix: bool = False) -> T:
103        """Parses a version string with prefix, for example: v0.45.0-dev (f01773cb1) or v0.115.0-dev.0"""
104        return cls.parse(version=version, drop_dev_suffix=drop_dev_suffix)
105
106    @classmethod
107    def parse_cargo(cls) -> MzVersion:
108        """Uses the cargo mz-environmentd package info to get the version of current source code state"""
109        metadata = json.loads(
110            subprocess.check_output(
111                ["cargo", "metadata", "--no-deps", "--format-version=1"]
112            )
113        )
114        for package in metadata["packages"]:
115            if package["name"] == "mz-environmentd":
116                return cls.parse_without_prefix(package["version"])
117        else:
118            raise ValueError("No mz-environmentd version found in cargo metadata")

Version of Materialize, can be parsed from version string, SQL, cargo

@classmethod
def get_prefix(cls) -> str:
97    @classmethod
98    def get_prefix(cls) -> str:
99        return "v"
@classmethod
def parse_mz(cls: type[~T], version: str, drop_dev_suffix: bool = False) -> ~T:
101    @classmethod
102    def parse_mz(cls: type[T], version: str, drop_dev_suffix: bool = False) -> T:
103        """Parses a version string with prefix, for example: v0.45.0-dev (f01773cb1) or v0.115.0-dev.0"""
104        return cls.parse(version=version, drop_dev_suffix=drop_dev_suffix)

Parses a version string with prefix, for example: v0.45.0-dev (f01773cb1) or v0.115.0-dev.0

@classmethod
def parse_cargo(cls) -> MzVersion:
106    @classmethod
107    def parse_cargo(cls) -> MzVersion:
108        """Uses the cargo mz-environmentd package info to get the version of current source code state"""
109        metadata = json.loads(
110            subprocess.check_output(
111                ["cargo", "metadata", "--no-deps", "--format-version=1"]
112            )
113        )
114        for package in metadata["packages"]:
115            if package["name"] == "mz-environmentd":
116                return cls.parse_without_prefix(package["version"])
117        else:
118            raise ValueError("No mz-environmentd version found in cargo metadata")

Uses the cargo mz-environmentd package info to get the version of current source code state

class MzCliVersion(TypedVersionBase):
121class MzCliVersion(TypedVersionBase):
122    """Version of Materialize APT"""
123
124    @classmethod
125    def get_prefix(cls) -> str:
126        return "mz-v"

Version of Materialize APT

@classmethod
def get_prefix(cls) -> str:
124    @classmethod
125    def get_prefix(cls) -> str:
126        return "mz-v"
class MzLspServerVersion(TypedVersionBase):
129class MzLspServerVersion(TypedVersionBase):
130    """Version of Materialize LSP Server"""
131
132    @classmethod
133    def get_prefix(cls) -> str:
134        return "mz-lsp-server-v"

Version of Materialize LSP Server

@classmethod
def get_prefix(cls) -> str:
132    @classmethod
133    def get_prefix(cls) -> str:
134        return "mz-lsp-server-v"
class MzDebugVersion(TypedVersionBase):
137class MzDebugVersion(TypedVersionBase):
138    """Version of Materialize Debug Tool"""
139
140    @classmethod
141    def get_prefix(cls) -> str:
142        return "mz-debug-v"

Version of Materialize Debug Tool

@classmethod
def get_prefix(cls) -> str:
140    @classmethod
141    def get_prefix(cls) -> str:
142        return "mz-debug-v"