Create Bazel gevent test harness (#27507)
* WIP * Add gevent test suite run under Bazel. * Fix things up * Yapf * Fix up Bazel files * Make py_grpc_test fancier * Attempt to fix Windows RBE * Attempt to kick GitHub * Fix Python 2 runs * Yet more fixes * And the patch file too * I am an idiot * Mark gevent tests flaky * Try to make rules_python more tolerant * Typo * Exclude reconnect test from gevent * Remove unnecessary parts of patch * Buildifier * You saw nothing * isort * Move py_grpc_test to an internal-only file * Review comments * More reviewer comments * Reviewpull/27644/head
parent
76dd0474e4
commit
7aa43b7a55
20 changed files with 297 additions and 14 deletions
@ -0,0 +1,60 @@ |
|||||||
|
# Copyright 2021 The gRPC Authors |
||||||
|
# |
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
# you may not use this file except in compliance with the License. |
||||||
|
# You may obtain a copy of the License at |
||||||
|
# |
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
# |
||||||
|
# Unless required by applicable law or agreed to in writing, software |
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
# See the License for the specific language governing permissions and |
||||||
|
# limitations under the License. |
||||||
|
|
||||||
|
import grpc |
||||||
|
import unittest |
||||||
|
import sys |
||||||
|
import os |
||||||
|
import pkgutil |
||||||
|
|
||||||
|
from typing import Sequence |
||||||
|
|
||||||
|
class SingleLoader(object): |
||||||
|
def __init__(self, pattern: str): |
||||||
|
loader = unittest.TestLoader() |
||||||
|
self.suite = unittest.TestSuite() |
||||||
|
tests = [] |
||||||
|
for importer, module_name, is_package in pkgutil.walk_packages([os.path.dirname(os.path.relpath(__file__))]): |
||||||
|
if pattern in module_name: |
||||||
|
module = importer.find_module(module_name).load_module(module_name) |
||||||
|
tests.append(loader.loadTestsFromModule(module)) |
||||||
|
if len(tests) != 1: |
||||||
|
raise AssertionError("Expected only 1 test module. Found {}".format(tests)) |
||||||
|
self.suite.addTest(tests[0]) |
||||||
|
|
||||||
|
|
||||||
|
def loadTestsFromNames(self, names: Sequence[str], module: str = None) -> unittest.TestSuite: |
||||||
|
return self.suite |
||||||
|
|
||||||
|
if __name__ == "__main__": |
||||||
|
from gevent import monkey |
||||||
|
|
||||||
|
monkey.patch_all() |
||||||
|
|
||||||
|
import grpc.experimental.gevent |
||||||
|
grpc.experimental.gevent.init_gevent() |
||||||
|
import gevent |
||||||
|
|
||||||
|
if len(sys.argv) != 2: |
||||||
|
print(f"USAGE: {sys.argv[0]} TARGET_MODULE", file=sys.stderr) |
||||||
|
|
||||||
|
target_module = sys.argv[1] |
||||||
|
|
||||||
|
loader = SingleLoader(target_module) |
||||||
|
runner = unittest.TextTestRunner() |
||||||
|
|
||||||
|
result = gevent.spawn(runner.run, loader.suite) |
||||||
|
result.join() |
||||||
|
if not result.value.wasSuccessful(): |
||||||
|
sys.exit("Test failure.") |
@ -0,0 +1,71 @@ |
|||||||
|
# Copyright 2021 The gRPC Authors |
||||||
|
# |
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
# you may not use this file except in compliance with the License. |
||||||
|
# You may obtain a copy of the License at |
||||||
|
# |
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
# |
||||||
|
# Unless required by applicable law or agreed to in writing, software |
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
# See the License for the specific language governing permissions and |
||||||
|
# limitations under the License. |
||||||
|
load("@grpc_python_dependencies//:requirements.bzl", "requirement") |
||||||
|
|
||||||
|
_GRPC_LIB = "//src/python/grpcio/grpc:grpcio" |
||||||
|
|
||||||
|
_COPIED_MAIN_SUFFIX = ".gevent.main" |
||||||
|
|
||||||
|
def py_grpc_gevent_test( |
||||||
|
name, |
||||||
|
srcs, |
||||||
|
main = None, |
||||||
|
deps = None, |
||||||
|
data = None, |
||||||
|
**kwargs): |
||||||
|
if main == None: |
||||||
|
if len(srcs) != 1: |
||||||
|
fail("When main is not provided, srcs must be of size 1.") |
||||||
|
main = srcs[0] |
||||||
|
deps = [] if deps == None else deps |
||||||
|
data = [] if data == None else data |
||||||
|
lib_name = name + ".gevent.lib" |
||||||
|
supplied_python_version = kwargs.pop("python_version", "") |
||||||
|
if supplied_python_version and supplied_python_version != "PY3": |
||||||
|
fail("py_grpc_gevent_test only supports python_version=PY3") |
||||||
|
native.py_library( |
||||||
|
name = lib_name, |
||||||
|
srcs = srcs, |
||||||
|
) |
||||||
|
augmented_deps = deps + [ |
||||||
|
":{}".format(lib_name), |
||||||
|
requirement("gevent"), |
||||||
|
] |
||||||
|
if _GRPC_LIB not in augmented_deps: |
||||||
|
augmented_deps.append(_GRPC_LIB) |
||||||
|
|
||||||
|
# The main file needs to be in the same package as the test file. |
||||||
|
copied_main_name = name + _COPIED_MAIN_SUFFIX |
||||||
|
copied_main_filename = copied_main_name + ".py" |
||||||
|
native.genrule( |
||||||
|
name = copied_main_name, |
||||||
|
srcs = ["//bazel:_gevent_test_main.py"], |
||||||
|
outs = [copied_main_filename], |
||||||
|
cmd = "cp $< $@", |
||||||
|
) |
||||||
|
|
||||||
|
# TODO(https://github.com/grpc/grpc/issues/27542): Remove once gevent is deemed non-flaky. |
||||||
|
if "flaky" in kwargs: |
||||||
|
kwargs.pop("flaky") |
||||||
|
|
||||||
|
native.py_test( |
||||||
|
name = name + ".gevent", |
||||||
|
args = [name], |
||||||
|
deps = augmented_deps, |
||||||
|
srcs = [copied_main_filename], |
||||||
|
main = copied_main_filename, |
||||||
|
python_version = "PY3", |
||||||
|
flaky = True, |
||||||
|
**kwargs |
||||||
|
) |
@ -0,0 +1,35 @@ |
|||||||
|
# Copyright 2021 The gRPC Authors |
||||||
|
# |
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
# you may not use this file except in compliance with the License. |
||||||
|
# You may obtain a copy of the License at |
||||||
|
# |
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
# |
||||||
|
# Unless required by applicable law or agreed to in writing, software |
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
# See the License for the specific language governing permissions and |
||||||
|
# limitations under the License. |
||||||
|
"""Python-related rules intended only for use internal to the repo.""" |
||||||
|
|
||||||
|
load("//bazel:gevent_test.bzl", "py_grpc_gevent_test") |
||||||
|
load("//bazel:python_rules.bzl", "py2and3_test") |
||||||
|
|
||||||
|
def internal_py_grpc_test(name, **kwargs): |
||||||
|
"""Runs a test under all supported environments.""" |
||||||
|
py2and3_test(name, **kwargs) |
||||||
|
py_grpc_gevent_test(name, **kwargs) |
||||||
|
|
||||||
|
suite_kwargs = {} |
||||||
|
if "visibility" in kwargs: |
||||||
|
suite_kwargs["visibility"] = kwargs["visibility"] |
||||||
|
|
||||||
|
native.test_suite( |
||||||
|
name = name, |
||||||
|
tests = [ |
||||||
|
name + ".both_pythons", |
||||||
|
name + ".gevent", |
||||||
|
], |
||||||
|
**suite_kwargs |
||||||
|
) |
@ -0,0 +1,76 @@ |
|||||||
|
diff --git a/python/pip_install/pip_repository.bzl b/python/pip_install/pip_repository.bzl
|
||||||
|
index c3007e1..f8a9234 100644
|
||||||
|
--- a/python/pip_install/pip_repository.bzl
|
||||||
|
+++ b/python/pip_install/pip_repository.bzl
|
||||||
|
@@ -39,7 +39,8 @@ def _resolve_python_interpreter(rctx):
|
||||||
|
if "/" not in python_interpreter:
|
||||||
|
python_interpreter = rctx.which(python_interpreter)
|
||||||
|
if not python_interpreter:
|
||||||
|
- fail("python interpreter not found")
|
||||||
|
+ print("WARNING: python interpreter not found. Python targets will not be functional")
|
||||||
|
+ return ""
|
||||||
|
return python_interpreter
|
||||||
|
|
||||||
|
def _parse_optional_attrs(rctx, args):
|
||||||
|
@@ -93,13 +94,49 @@ def _parse_optional_attrs(rctx, args):
|
||||||
|
|
||||||
|
return args
|
||||||
|
|
||||||
|
+def _generate_stub_requirements_bzl(rctx):
|
||||||
|
+ contents = """\
|
||||||
|
+def requirement(name):
|
||||||
|
+ return "@{repo}//:empty"
|
||||||
|
+""".format(repo=rctx.attr.name)
|
||||||
|
+ rctx.file("requirements.bzl", contents)
|
||||||
|
+
|
||||||
|
_BUILD_FILE_CONTENTS = """\
|
||||||
|
package(default_visibility = ["//visibility:public"])
|
||||||
|
|
||||||
|
# Ensure the `requirements.bzl` source can be accessed by stardoc, since users load() from it
|
||||||
|
exports_files(["requirements.bzl"])
|
||||||
|
+
|
||||||
|
+py_library(
|
||||||
|
+ name = "empty",
|
||||||
|
+ srcs = [],
|
||||||
|
+)
|
||||||
|
"""
|
||||||
|
|
||||||
|
+def _python_version_info(rctx, python_interpreter, info_index):
|
||||||
|
+ cmd = [
|
||||||
|
+ python_interpreter,
|
||||||
|
+ "-c",
|
||||||
|
+ "from __future__ import print_function; import sys; print(sys.version_info[{}])".format(info_index)
|
||||||
|
+ ]
|
||||||
|
+ result = rctx.execute(cmd)
|
||||||
|
+ if result.stderr or not result.stdout:
|
||||||
|
+ print("WARNING: Failed to get version info from {}".format(python_interpreter))
|
||||||
|
+ return None
|
||||||
|
+ return int(result.stdout.strip())
|
||||||
|
+
|
||||||
|
+def _python_version_supported(rctx, python_interpreter):
|
||||||
|
+ major_version = _python_version_info(rctx, python_interpreter, 0)
|
||||||
|
+ minor_version = _python_version_info(rctx, python_interpreter, 1)
|
||||||
|
+ if major_version == None or minor_version == None:
|
||||||
|
+ print("WARNING: Failed to get Python version of {}".format(python_interpreter))
|
||||||
|
+ return False
|
||||||
|
+ if (major_version != 3 or minor_version < 6):
|
||||||
|
+ print("WARNING: {} is of version {}.{}. This version is unsupported.".format(python_interpreter, major_version, minor_version))
|
||||||
|
+ return False
|
||||||
|
+ return True
|
||||||
|
+
|
||||||
|
+
|
||||||
|
def _pip_repository_impl(rctx):
|
||||||
|
python_interpreter = _resolve_python_interpreter(rctx)
|
||||||
|
|
||||||
|
@@ -109,6 +146,11 @@ def _pip_repository_impl(rctx):
|
||||||
|
# We need a BUILD file to load the generated requirements.bzl
|
||||||
|
rctx.file("BUILD.bazel", _BUILD_FILE_CONTENTS)
|
||||||
|
|
||||||
|
+ # Check if python interpreter has minimum required version.
|
||||||
|
+ if not python_interpreter or not _python_version_supported(rctx, python_interpreter):
|
||||||
|
+ _generate_stub_requirements_bzl(rctx)
|
||||||
|
+ return
|
||||||
|
+
|
||||||
|
pypath = _construct_pypath(rctx)
|
||||||
|
|
||||||
|
if rctx.attr.incremental:
|
Loading…
Reference in new issue