From df247fb33a3a3f974664c0079663069c5dea2d20 Mon Sep 17 00:00:00 2001 From: Richard Belleville Date: Mon, 10 Feb 2020 16:47:43 -0800 Subject: [PATCH] Experimental API decorator --- src/python/grpcio/grpc/_simple_stubs.py | 9 ++++--- .../grpcio/grpc/experimental/__init__.py | 25 ++++++++++++++++--- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/python/grpcio/grpc/_simple_stubs.py b/src/python/grpcio/grpc/_simple_stubs.py index 3edf72c3422..378c234cd15 100644 --- a/src/python/grpcio/grpc/_simple_stubs.py +++ b/src/python/grpcio/grpc/_simple_stubs.py @@ -22,6 +22,7 @@ from typing import (Any, AnyStr, Callable, Dict, Iterator, Optional, Sequence, Tuple, TypeVar, Union) import grpc +from grpc.experimental import experimental_api RequestType = TypeVar('RequestType') ResponseType = TypeVar('ResponseType') @@ -169,6 +170,7 @@ class ChannelCache: # pylint: disable=too-many-arguments +@experimental_api def unary_unary( request: RequestType, target: str, @@ -228,7 +230,6 @@ def unary_unary( Returns: The response to the RPC. """ - grpc.experimental.warn_experimental("unary_unary") channel = ChannelCache.get().get_channel(target, options, channel_credentials, compression) multicallable = channel.unary_unary(method, request_serializer, @@ -241,6 +242,7 @@ def unary_unary( # pylint: disable=too-many-arguments +@experimental_api def unary_stream( request: RequestType, target: str, @@ -299,7 +301,6 @@ def unary_stream( Returns: An iterator of responses. """ - grpc.experimental.warn_experimental("unary_stream") channel = ChannelCache.get().get_channel(target, options, channel_credentials, compression) multicallable = channel.unary_stream(method, request_serializer, @@ -312,6 +313,7 @@ def unary_stream( # pylint: disable=too-many-arguments +@experimental_api def stream_unary( request_iterator: Iterator[RequestType], target: str, @@ -370,7 +372,6 @@ def stream_unary( Returns: The response to the RPC. """ - grpc.experimental.warn_experimental("stream_unary") channel = ChannelCache.get().get_channel(target, options, channel_credentials, compression) multicallable = channel.stream_unary(method, request_serializer, @@ -383,6 +384,7 @@ def stream_unary( # pylint: disable=too-many-arguments +@experimental_api def stream_stream( request_iterator: Iterator[RequestType], target: str, @@ -441,7 +443,6 @@ def stream_stream( Returns: An iterator of responses. """ - grpc.experimental.warn_experimental("stream_stream") channel = ChannelCache.get().get_channel(target, options, channel_credentials, compression) multicallable = channel.stream_stream(method, request_serializer, diff --git a/src/python/grpcio/grpc/experimental/__init__.py b/src/python/grpcio/grpc/experimental/__init__.py index 29f499f57c6..93ed0f64291 100644 --- a/src/python/grpcio/grpc/experimental/__init__.py +++ b/src/python/grpcio/grpc/experimental/__init__.py @@ -16,11 +16,14 @@ These APIs are subject to be removed during any minor version release. """ +import functools import sys import warnings import grpc +_EXPERIMENTAL_APIS_USED = set() + class ChannelOptions(object): """Indicates a channel option unique to gRPC Python. @@ -56,16 +59,30 @@ class ExperimentalApiWarning(Warning): """A warning that an API is experimental.""" -def warn_experimental(api_name): - msg = ("{} is an experimental API. It is subject to change or ".format( - api_name) + "removal between minor releases. Proceed with caution.") - warnings.warn(msg, ExperimentalApiWarning, stacklevel=2) +def _warn_experimental(api_name, stack_offset): + if api_name not in _EXPERIMENTAL_APIS_USED: + _EXPERIMENTAL_APIS_USED.add(api_name) + msg = ("'{}' is an experimental API. It is subject to change or ". + format(api_name) + + "removal between minor releases. Proceed with caution.") + warnings.warn(msg, ExperimentalApiWarning, stacklevel=2 + stack_offset) + + +def experimental_api(f): + + @functools.wraps(f) + def _wrapper(*args, **kwargs): + _warn_experimental(f.__name__, 1) + return f(*args, **kwargs) + + return _wrapper __all__ = ( 'ChannelOptions', 'ExperimentalApiWarning', 'UsageError', + 'experimental_api', 'insecure_channel_credentials', )