github.com/apache/beam/sdks/v2@v2.48.2/python/apache_beam/utils/urns.py (about) 1 # 2 # Licensed to the Apache Software Foundation (ASF) under one or more 3 # contributor license agreements. See the NOTICE file distributed with 4 # this work for additional information regarding copyright ownership. 5 # The ASF licenses this file to You under the Apache License, Version 2.0 6 # (the "License"); you may not use this file except in compliance with 7 # the License. You may obtain a copy of the License at 8 # 9 # http://www.apache.org/licenses/LICENSE-2.0 10 # 11 # Unless required by applicable law or agreed to in writing, software 12 # distributed under the License is distributed on an "AS IS" BASIS, 13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 # See the License for the specific language governing permissions and 15 # limitations under the License. 16 # 17 18 """For internal use only; no backwards-compatibility guarantees.""" 19 20 # pytype: skip-file 21 22 # TODO(https://github.com/apache/beam/issues/18399): Issue with dill + local 23 # classes + abc metaclass 24 # import abc 25 import inspect 26 from typing import TYPE_CHECKING 27 from typing import Any 28 from typing import Callable 29 from typing import Dict 30 from typing import Optional 31 from typing import Tuple 32 from typing import Type 33 from typing import TypeVar 34 from typing import Union 35 from typing import overload 36 37 from google.protobuf import message 38 from google.protobuf import wrappers_pb2 39 40 from apache_beam.internal import pickler 41 from apache_beam.utils import proto_utils 42 43 if TYPE_CHECKING: 44 from apache_beam.portability.api import beam_runner_api_pb2 45 from apache_beam.runners.pipeline_context import PipelineContext 46 47 T = TypeVar('T') 48 RunnerApiFnT = TypeVar('RunnerApiFnT', bound='RunnerApiFn') 49 ConstructorFn = Callable[[Union['message.Message', bytes], 'PipelineContext'], 50 Any] 51 52 53 class RunnerApiFn(object): 54 """Abstract base class that provides urn registration utilities. 55 56 A class that inherits from this class will get a registration-based 57 from_runner_api and to_runner_api method that convert to and from 58 beam_runner_api_pb2.FunctionSpec. 59 60 Additionally, register_pickle_urn can be called from the body of a class 61 to register serialization via pickling. 62 """ 63 64 # TODO(https://github.com/apache/beam/issues/18399): Issue with dill + local 65 # classes + abc metaclass 66 # __metaclass__ = abc.ABCMeta 67 68 _known_urns = {} # type: Dict[str, Tuple[Optional[type], ConstructorFn]] 69 70 # @abc.abstractmethod is disabled here to avoid an error with mypy. mypy 71 # performs abc.abtractmethod/property checks even if a class does 72 # not use abc.ABCMeta, however, functions like `register_pickle_urn` 73 # dynamically patch `to_runner_api_parameter`, which mypy cannot track, so 74 # mypy incorrectly infers that this method has not been overridden with a 75 # concrete implementation. 76 # @abc.abstractmethod 77 def to_runner_api_parameter(self, unused_context): 78 # type: (PipelineContext) -> Tuple[str, Any] 79 80 """Returns the urn and payload for this Fn. 81 82 The returned urn(s) should be registered with `register_urn`. 83 """ 84 raise NotImplementedError 85 86 @classmethod 87 @overload 88 def register_urn( 89 cls, 90 urn, # type: str 91 parameter_type, # type: Type[T] 92 ): 93 # type: (...) -> Callable[[Callable[[T, PipelineContext], Any]], Callable[[T, PipelineContext], Any]] 94 pass 95 96 @classmethod 97 @overload 98 def register_urn( 99 cls, 100 urn, # type: str 101 parameter_type, # type: None 102 ): 103 # type: (...) -> Callable[[Callable[[bytes, PipelineContext], Any]], Callable[[bytes, PipelineContext], Any]] 104 pass 105 106 @classmethod 107 @overload 108 def register_urn(cls, 109 urn, # type: str 110 parameter_type, # type: Type[T] 111 fn # type: Callable[[T, PipelineContext], Any] 112 ): 113 # type: (...) -> None 114 pass 115 116 @classmethod 117 @overload 118 def register_urn(cls, 119 urn, # type: str 120 parameter_type, # type: None 121 fn # type: Callable[[bytes, PipelineContext], Any] 122 ): 123 # type: (...) -> None 124 pass 125 126 @classmethod 127 def register_urn(cls, urn, parameter_type, fn=None): 128 """Registers a urn with a constructor. 129 130 For example, if 'beam:fn:foo' had parameter type FooPayload, one could 131 write `RunnerApiFn.register_urn('bean:fn:foo', FooPayload, foo_from_proto)` 132 where foo_from_proto took as arguments a FooPayload and a PipelineContext. 133 This function can also be used as a decorator rather than passing the 134 callable in as the final parameter. 135 136 A corresponding to_runner_api_parameter method would be expected that 137 returns the tuple ('beam:fn:foo', FooPayload) 138 """ 139 def register(fn): 140 cls._known_urns[urn] = parameter_type, fn 141 return fn 142 143 if fn: 144 # Used as a statement. 145 register(fn) 146 else: 147 # Used as a decorator. 148 return register 149 150 @classmethod 151 def register_pickle_urn(cls, pickle_urn): 152 """Registers and implements the given urn via pickling. 153 """ 154 inspect.currentframe().f_back.f_locals['to_runner_api_parameter'] = ( 155 lambda self, 156 context: 157 (pickle_urn, wrappers_pb2.BytesValue(value=pickler.dumps(self)))) 158 cls.register_urn( 159 pickle_urn, 160 wrappers_pb2.BytesValue, 161 lambda proto, 162 unused_context: pickler.loads(proto.value)) 163 164 def to_runner_api(self, context): 165 # type: (PipelineContext) -> beam_runner_api_pb2.FunctionSpec 166 167 """Returns an FunctionSpec encoding this Fn. 168 169 Prefer overriding self.to_runner_api_parameter. 170 """ 171 from apache_beam.portability.api import beam_runner_api_pb2 172 urn, typed_param = self.to_runner_api_parameter(context) 173 return beam_runner_api_pb2.FunctionSpec( 174 urn=urn, 175 payload=typed_param.SerializeToString() if isinstance( 176 typed_param, message.Message) else typed_param) 177 178 @classmethod 179 def from_runner_api(cls, fn_proto, context): 180 # type: (Type[RunnerApiFnT], beam_runner_api_pb2.FunctionSpec, PipelineContext) -> RunnerApiFnT 181 182 """Converts from an FunctionSpec to a Fn object. 183 184 Prefer registering a urn with its parameter type and constructor. 185 """ 186 parameter_type, constructor = cls._known_urns[fn_proto.urn] 187 return constructor( 188 proto_utils.parse_Bytes(fn_proto.payload, parameter_type), context)