Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test(sdk): make protobuf version requirements more granular #4479

Merged
merged 11 commits into from Dec 5, 2022
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:
dmitryduev marked this conversation as resolved.
Show resolved Hide resolved
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)