From 5b9a80be2b52cfd7638c856a1a3311c9d2edf2ec Mon Sep 17 00:00:00 2001 From: Dmitry Duev Date: Mon, 5 Dec 2022 14:07:45 -0800 Subject: [PATCH] test(sdk): make protobuf version requirements more granular (#4479) test(sdk): make protobuf version requirements more granular --- .circleci/config.yml | 49 ++++++- requirements.txt | 5 +- tools/check-protobuf-version-compatibility.py | 134 ++++++++++++++++++ 3 files changed, 185 insertions(+), 3 deletions(-) create mode 100644 tools/check-protobuf-version-compatibility.py diff --git a/.circleci/config.yml b/.circleci/config.yml index d2f4ff5188c..7d22cc15663 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -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:<>.<>" + 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: @@ -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<><>" + # # standalone GPU tests on Windows # - win: @@ -1307,6 +1343,7 @@ workflows: - pex: name: "pex" + # todo: needs love # manual_test: # when: << pipeline.parameters.manual_test >> @@ -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<><>" + # # standalone GPU tests on Windows # - win: @@ -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: diff --git a/requirements.txt b/requirements.txt index 8adb09f58e7..342612531a8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -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 diff --git a/tools/check-protobuf-version-compatibility.py b/tools/check-protobuf-version-compatibility.py new file mode 100644 index 00000000000..04a2991e5ab --- /dev/null +++ b/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)