Skip to content

Commit

Permalink
fix: protobuf version guessing, fixes #143
Browse files Browse the repository at this point in the history
During the build process, protoc is invoked to build protobuf files.
These built files are deployed to users, and thus the users require a
compatible python-protobuf version.

For that, the required protobuf version is set during the build process
in setup.py. However, this version selection is not up to date with the
versioning scheme used by protobuf. protobuf keeps the major version
independent between languages, and minor/patch versions in sync.
E.g., as of the time of writing, python-protobuf 4.21.12 is compatible
with libprotoc 3.21.12.
see: https://protobuf.dev/news/2022-05-06/

However, it is not clear under which circumstances these versions are
compatible to each other. (I.e., even if minor/patch version are
different, the binary format may still be compatible.)
This makes correctly predicting the required (potentially unrealeased)
python-protobuf version impossible.

Co-authored-by: Hannes T <80697868+s9105947@users.noreply.github.com>
  • Loading branch information
tilsche and s9105947 committed Mar 1, 2023
1 parent bfb84b1 commit 05ba859
Showing 1 changed file with 32 additions and 7 deletions.
39 changes: 32 additions & 7 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@
import re
import subprocess
import sys
from bisect import bisect_right
from distutils.errors import DistutilsFileError
from distutils.log import ERROR, INFO
from distutils.spawn import find_executable
from operator import itemgetter
from typing import Optional, Tuple

import mypy_protobuf
Expand Down Expand Up @@ -58,16 +60,39 @@ def protoc_version(protoc: str) -> Tuple[int, int, int]:
return tuple(int(version_search.group(g)) for g in ("major", "minor", "patch"))


def make_protobuf_requirement(major: int, minor: int, patch: int) -> str:
"""Sometimes the versions of libprotoc and the python package `protobuf` are out of sync.
# This encodes on which minor protobuf version the major python protobuf
# version was bumped
protobuf_version_mapping = (
(3, 0),
(4, 21),
)


For example, while there was protoc version 3.12.3, the latest release of
`protobuf` was 3.12.2. So we'll just depend on `x.y.0` and hope that
there's no breaking changes between patches.
def make_protobuf_requirement(major: int, minor: int, patch: int) -> str:
"""
We need to figure out a compatible version range of the python protobuf
package based on the version of the installed `protoc`. However, there is
no clean way to determine if those versions are compatible.
Between protobuf packages for different languages/libprotoc major versions
may diverge for the same release, while minor and patch versions align.
Different releases may or may not be compatible...
We can not predict which python protobuf versions will be compatible.
Hence, for compatibility this uses the following approach:
We hardcode a minor->major map, so the build works sort of like this
1) get m(major).n(minor).p(patch) from protoc
2) m' = 3 if n < 21 else 4 (as encoded in protobuf_version_mapping)
2) depend on protobuf>=m'.n,<m'.n+1
See also:
- https://github.com/protocolbuffers/protobuf/issues/11123
- https://protobuf.dev/news/2022-05-06/#python-updates
"""
del patch # We don't even care

del patch
return f"protobuf~={major}.{minor}.{0}"
# We must subtract one because bisect gives the insertion point after...
py_major = protobuf_version_mapping[
bisect_right(protobuf_version_mapping, minor, key=itemgetter(1)) - 1
][0]
return f"protobuf>={py_major}.{minor}, <{py_major}.{minor+1}"


def get_protobuf_requirement() -> str:
Expand Down

0 comments on commit 05ba859

Please sign in to comment.