Skip to content

Commit

Permalink
test(sdk): make protobuf version requirements more granular (#4479)
Browse files Browse the repository at this point in the history
test(sdk): make protobuf version requirements more granular
  • Loading branch information
dmitryduev committed Dec 5, 2022
1 parent 202de65 commit 5b9a80b
Show file tree
Hide file tree
Showing 3 changed files with 185 additions and 3 deletions.
49 changes: 47 additions & 2 deletions .circleci/config.yml
Expand Up @@ -366,6 +366,33 @@ jobs:
./wandb.pex -c "import wandb; wandb.init(mode='offline'); wandb.finish()"
- save-test-results

protobuf-compatability:
parameters:
python_version_major:
type: integer
default: 3
python_version_minor:
type: integer
default: 8
docker:
- image: "python:<<parameters.python_version_major>>.<<parameters.python_version_minor>>"
resource_class: small
working_directory: /mnt/ramdisk
steps:
- checkout
- run:
name: Install system deps
command: |
apt-get update && apt-get install -y libsndfile1 ffmpeg
- run:
name: Check protobuf version compatibility
command: |
pip install -U pip
pip install .
python tools/check-protobuf-version-compatibility.py
no_output_timeout: 5m
- save-test-results

win:
parameters:
python_version_major:
Expand Down Expand Up @@ -1062,6 +1089,15 @@ workflows:
notify_on_failure: true
notify_on_failure_channel: $SLACK_SDK_NIGHTLY_CI_GROWTH_CHANNEL
#
# protobuf compatibility tests
#
- protobuf-compatability:
matrix:
parameters:
python_version_major: [ 3 ]
python_version_minor: [ 7, 8, 9, 10 ]
name: "protobuf-compatability-py<<matrix.python_version_major>><<matrix.python_version_minor>>"
#
# standalone GPU tests on Windows
#
- win:
Expand Down Expand Up @@ -1307,6 +1343,7 @@ workflows:
- pex:
name: "pex"


# todo: needs love
# manual_test:
# when: << pipeline.parameters.manual_test >>
Expand Down Expand Up @@ -1376,6 +1413,15 @@ workflows:
notify_on_failure: true
notify_on_failure_channel: $SLACK_SDK_NIGHTLY_CI_GROWTH_CHANNEL
#
# protobuf compatibility tests
#
- protobuf-compatability:
matrix:
parameters:
python_version_major: [ 3 ]
python_version_minor: [ 7, 8, 9, 10 ]
name: "protobuf-compatability-py<<matrix.python_version_major>><<matrix.python_version_minor>>"
#
# standalone GPU tests on Windows
#
- win:
Expand Down Expand Up @@ -1403,8 +1449,7 @@ workflows:
- pip_install_wandb:
matrix:
parameters:
# todo: replace 3.11-rc with 3.11 when it's released on 2022-10-24
python_version: ["3.6", "3.7", "3.8", "3.9", "3.10", "3.11-rc"]
python_version: ["3.6", "3.7", "3.8", "3.9", "3.10", "3.11"]
context: slack-secrets
notify_on_failure: << pipeline.parameters.manual_nightly_slack_notify >>
requires:
Expand Down
5 changes: 4 additions & 1 deletion requirements.txt
Expand Up @@ -7,7 +7,10 @@ psutil>=5.0.0
sentry-sdk>=1.0.0
six>=1.13.0
docker-pycreds>=0.4.0
protobuf>=3.12.0,!=4.0.*,!=4.21.0,<5
protobuf>=3.12.0,!=4.21.0,<5; python_version < '3.9' and sys_platform == 'linux'
protobuf>=3.15.0,!=4.21.0,<5; python_version == '3.9' and sys_platform == 'linux'
protobuf>=3.19.0,!=4.21.0,<5; python_version > '3.9' and sys_platform == 'linux'
protobuf>=3.19.0,!=4.21.0,<5; sys_platform != 'linux'
PyYAML
pathtools # supports vendored version of watchdog 0.9.0
setproctitle
Expand Down
134 changes: 134 additions & 0 deletions tools/check-protobuf-version-compatibility.py
@@ -0,0 +1,134 @@
import pathlib
import platform
import re
import subprocess
import sys
from typing import List, Tuple

from pkg_resources import parse_version # noqa: F401


def get_available_protobuf_versions() -> List[str]:
"""Get a list of available protobuf versions."""
try:
output = subprocess.check_output(
["pip", "index", "versions", "protobuf"],
).decode("utf-8")
versions = list({o for o in output.split() if o[0].isnumeric()})
versions = [v if not v.endswith(",") else v[:-1] for v in versions]
return sorted(versions)
except subprocess.CalledProcessError:
return []


def parse_protobuf_requirements() -> List[Tuple[str, str]]:
"""Parse protobuf requirements from a requirements.txt file."""
path_requirements = pathlib.Path(__file__).parent.parent / "requirements.txt"
with open(path_requirements) as f:
requirements = f.readlines()

system_python_version = f"{sys.version_info.major}.{sys.version_info.minor}"
system_platform = sys.platform
system_machine = platform.machine()

protobuf_reqs = []
for line in requirements:
if line.startswith("protobuf"):
version_reqs = line.strip().split(";")[0]

# first, check the system requirements
system_reqs = line.strip().split(";")[1]
# regex to find quoted python version in system_reqs
python_version = re.search(
r"python_version\s+([<>=!]+)\s+[',\"]([2,3]*[.][0-9]+)[',\"]",
system_reqs,
)
if python_version is not None:
version_check = (
f"parse_version('{system_python_version}') "
f"{python_version.group(1)} "
f"parse_version('{python_version.group(2)}')"
)
if not eval(version_check):
continue
# regex to find quoted platform in system_reqs
platform_reqs = re.search(
r"sys_platform\s+([<>=!]+)\s+[',\"]([a-z]+)[',\"]",
system_reqs,
)

if platform_reqs is not None:
if not eval(
f"'{system_platform}' {platform_reqs.group(1)} '{platform_reqs.group(2)}'"
):
continue

# regex to find platform machine in system_reqs
platform_machine = re.search(
r"platform[.]machine\s+([<>=!]+)\s+[',\"]([a-z]+)[',\"]",
system_reqs,
)
if platform_machine is not None:
if not eval(
f"'{system_machine}' {platform_machine.group(1)} '{platform_machine.group(2)}'"
):
continue

# finally, parse the protobuf version requirements
reqs = version_reqs.split("protobuf")[1].split(",")
print(reqs)
for req in reqs:
for i, char in enumerate(req):
if char.isnumeric():
protobuf_reqs.append(
(
req[:i].strip(),
req[i:].strip(),
)
)
break
print(protobuf_reqs)
return protobuf_reqs


def get_matching_versions(
available_protobuf_vs: List[str], protobuf_reqs: List[Tuple[str, str]]
) -> List[str]:
matching_vs = []
for v in available_protobuf_vs:
if all(
eval(f"parse_version('{v}') {rq[0]} parse_version('{rq[1]}')")
for rq in protobuf_reqs
):
matching_vs.append(v)

return sorted(list(set(matching_vs)))


def attempt_install_protobuf_version(version: str) -> bool:
try:
subprocess.check_call(["pip", "install", f"protobuf=={version}"])
subprocess.check_call(["python", "-c", "import wandb"])
return True
except subprocess.CalledProcessError:
return False


if __name__ == "__main__":
available_protobuf_versions = get_available_protobuf_versions()
protobuf_requirements = parse_protobuf_requirements()
matching_versions = get_matching_versions(
available_protobuf_versions,
protobuf_requirements,
)

version_compatibility = {
version: attempt_install_protobuf_version(version)
for version in matching_versions
}

for version, compatible in version_compatibility.items():
print(f"protobuf=={version}: {compatible}")

if not all(version_compatibility.values()):
sys.exit(1)

0 comments on commit 5b9a80b

Please sign in to comment.