diff --git a/setup.py b/setup.py index 1449bf5..92a5caa 100644 --- a/setup.py +++ b/setup.py @@ -18,6 +18,8 @@ "attrs>=21.2.0", "frozendict>=1.2", "google-auth>=1.31.0", + "grpcio>=1.50.0", + "grpcio-tools>=1.50.0", "mock>=4.0.3", "Pillow>=8.3.1", "protobuf>=3.19.0", diff --git a/src/google/appengine/api/images/__init__.py b/src/google/appengine/api/images/__init__.py index a368e56..44c91f6 100755 --- a/src/google/appengine/api/images/__init__.py +++ b/src/google/appengine/api/images/__init__.py @@ -33,17 +33,50 @@ import json +import logging +import os import struct +# import google.auth +# import google.auth.transport.grpc +# import google.auth.transport.requests +from google.oauth2 import id_token +import grpc from google.appengine.api import apiproxy_stub_map from google.appengine.api import blobstore from google.appengine.api import datastore_types -from google.appengine.api.images import images_service_pb2 +from . import images_service_pb2 +from . import images_service_rpc_pb2_grpc from google.appengine.runtime import apiproxy_errors import six from six.moves import range + +def _make_grpc_call(method_name, request): + """Creates an authenticated gRPC channel and makes an RPC call.""" + target_host = os.environ.get('IMAGES_GRPC_SERVICE_TARGET') + target_audience = os.environ.get('IMAGES_GRPC_SERVICE_TARGET_URL') + + credentials, project_id = google.auth.default() + auth_req = google.auth.transport.requests.Request() + credentials.refresh(auth_req) + token = id_token.fetch_id_token(auth_req, target_audience) + + channel_credentials = grpc.ssl_channel_credentials() + call_credentials = grpc.access_token_call_credentials(token) + composite_credentials = grpc.composite_channel_credentials( + channel_credentials, + call_credentials, + ) + + authed_channel = grpc.secure_channel(target_host, composite_credentials) + stub = images_service_rpc_pb2_grpc.ImagesServiceStub(authed_channel) + + grpc_method = getattr(stub, method_name) + return grpc_method(request) + + BlobKey = datastore_types.BlobKey @@ -851,6 +884,29 @@ def execute_transforms_async(self, ValueError: When `transparent_substitution_rgb` is not an integer. Error: All other error conditions. """ + env_value = os.environ.get('USE_CUSTOM_IMAGES_GRPC_SERVICE') + logging.warning(f"USE_CUSTOM_IMAGES_GRPC_SERVICE raw value: {env_value}") + logging.warning(f"USE_CUSTOM_IMAGES_GRPC_SERVICE boolean evaluation: {bool(env_value)}") + if env_value: + logging.warning('Using custom gRPC image service.') + if not self._transforms: + raise BadRequestError('Must specify at least one transformation.') + + request = images_service_pb2.ImagesTransformRequest() + self._set_imagedata(request.image) + for transform in self._transforms: + request.transform.add().CopyFrom(transform) + request.output.mime_type = output_encoding + if quality is not None: + request.output.quality = quality + + grpc_response = _make_grpc_call("Transform", request) + + class FakeRpc: + def get_result(self): + return grpc_response.image.content + return FakeRpc() + if output_encoding not in OUTPUT_ENCODING_TYPES: raise BadRequestError("Output encoding type not in recognized set " "%s" % OUTPUT_ENCODING_TYPES) @@ -983,6 +1039,19 @@ def histogram_async(self, rpc=None): LargeImageError: When the image data supplied is too large to process. Error: All other error conditions. """ + if os.environ.get('USE_CUSTOM_IMAGES_GRPC_SERVICE'): + logging.warning('Using custom gRPC image service for histogram.') + request = images_service_pb2.ImagesHistogramRequest() + self._set_imagedata(request.image) + + grpc_response = _make_grpc_call("Histogram", request) + + class FakeRpc: + def get_result(self): + histogram_ = grpc_response.histogram + return [histogram_.red, histogram_.green, histogram_.blue] + return FakeRpc() + request = images_service_pb2.ImagesHistogramRequest() response = images_service_pb2.ImagesHistogramResponse() @@ -1733,7 +1802,6 @@ def composite_async(inputs, image_map = {} request = images_service_pb2.ImagesCompositeRequest() - response = images_service_pb2.ImagesTransformResponse() for (image, x, y, opacity, anchor) in inputs: if not image: raise BadRequestError("Each input must include an image") @@ -1775,26 +1843,36 @@ def composite_async(inputs, (quality is not None)): request.canvas.output.quality = quality - def composite_hook(rpc): - """Checks success, handles exceptions, and returns the converted RPC result. - - Args: - rpc: A UserRPC object. - - Returns: - Images bytes of the composite image. + if os.environ.get('USE_CUSTOM_IMAGES_GRPC_SERVICE'): + logging.warning('Using custom gRPC image service for composite.') + grpc_response = _make_grpc_call("Composite", request) - Raises: - See `composite_async` for more details. - """ - try: - rpc.check_success() - except apiproxy_errors.ApplicationError as e: - raise _ToImagesError(e) - return rpc.response.image.content - - return _make_async_call(rpc, "Composite", request, response, composite_hook, - None) + class FakeRpc: + def get_result(self): + return grpc_response.image.content + return FakeRpc() + else: + response = images_service_pb2.ImagesTransformResponse() + def composite_hook(rpc): + """Checks success, handles exceptions, and returns the converted RPC result. + + Args: + rpc: A UserRPC object. + + Returns: + Images bytes of the composite image. + + Raises: + See `composite_async` for more details. + """ + try: + rpc.check_success() + except apiproxy_errors.ApplicationError as e: + raise _ToImagesError(e) + return rpc.response.image.content + + return _make_async_call(rpc, "Composite", request, response, composite_hook, + None) def histogram(image_data, rpc=None): diff --git a/src/google/appengine/api/images/images_service.proto b/src/google/appengine/api/images/images_service.proto new file mode 100644 index 0000000..5dd0c18 --- /dev/null +++ b/src/google/appengine/api/images/images_service.proto @@ -0,0 +1,239 @@ +// Copyright 2008 Google Inc. All Rights Reserved. +// Author: ttrimble@google.com (Troy Trimble) + +syntax = "proto2"; + +// Some generic_services option(s) added automatically. +// See: http://go/proto2-generic-services-default +package apphosting; + +option java_generic_services = true; // auto-added + +// option java_api_version = 2; +option java_package = "com.google.appengine.api.images"; +option java_outer_classname = "ImagesServicePb"; + +message ImagesServiceError { + enum ErrorCode { + UNSPECIFIED_ERROR = 1; + BAD_TRANSFORM_DATA = 2; + NOT_IMAGE = 3; + BAD_IMAGE_DATA = 4; + IMAGE_TOO_LARGE = 5; + INVALID_BLOB_KEY = 6; + + // Errors specific to Google Storage objects. + ACCESS_DENIED = 7; + OBJECT_NOT_FOUND = 8; + } +} + +message ImagesServiceTransform { + enum Type { + RESIZE = 1; + ROTATE = 2; + HORIZONTAL_FLIP = 3; + VERTICAL_FLIP = 4; + CROP = 5; + IM_FEELING_LUCKY = 6; + } +} + +// Next id is 15. +message Transform { + // Resize the given image to the given specificaiton. The service will try + // to resize the image to the minimum of the two sizes while keeping a + // consistent image ratio. + optional int32 width = 1; + optional int32 height = 2; + + // If both width and height are specified, and crop to fit is true, the less + // restrictiveof the two is used and the image is cropped to fit the other + // dimenstion. An offset for the center can be specified as well. + optional bool crop_to_fit = 11; // Per bool-type, default is false. + + // WARNING: Default is 0.5, must continue to be enforced in cc file. + // This default must be kept in this proto2 file as it affects the user + // facing libraries. + optional float crop_offset_x = 12 [default = 0.5]; // 0: left, 1: right. + + // WARNING: Default is 0.5, must continue to be enforced in cc file. + optional float crop_offset_y = 13 [default = 0.5]; // 0: top, 1: bottom. + + // Rotate an image some number of degrees (90, 180, 270) in the clockwise + // direction. + optional int32 rotate = 3; // Per int-type, default is 0. + + // Flip an image horizontally + optional bool horizontal_flip = 4; // Per bool-type, default is false. + + // Flip an image vertically + optional bool vertical_flip = 5; // Per bool-type, default is false. + + // Crop an image. + optional float crop_left_x = 6; // Per float-type, default is zero. + optional float crop_top_y = 7; // Per float-type, default is zero. + + // WARNING: Default is 1.0, must continue to be enforced in cc file. + // These defaults are kept for the sake of the py and other user library + // files. Editing risks user visible change. However, all non-user code + // (C++) should set 1.0 manually as the proto3 version of the proto has no + // concept of defaults from proto files. + optional float crop_right_x = 8 [default = 1.0]; + + // WARNING: Default is 1.0, must continue to be enforced in cc file. + optional float crop_bottom_y = 9 [default = 1.0]; + + // Autolevels (Picasa I'm Feeling Lucky) + optional bool autolevels = 10; // Per bool-type, default is false. + + // Allow stretching of the image to exactly fit the specified width and height + optional bool allow_stretch = 14; // Per bool-type, default is false. + + // N.B. We cannot remove these fields via the "reserved" keyword because + // doing so is not compatible with the python27_proto build rule. + optional bool deprecated_width_set = 101; + optional bool deprecated_height_set = 102; + optional bool deprecated_crop_offset_x_set = 112; + optional bool deprecated_crop_offset_y_set = 113; + optional bool deprecated_crop_right_x_set = 108; + optional bool deprecated_crop_bottom_y_set = 109; +} + +message ImageData { + required bytes content = 1 [ctype = CORD]; + + // If the content is empty, and a valid blob_key is provided, + // perform the transform on the specified blob instead, if possible. + optional string blob_key = 2; + + // Thumbnailer can return the result image width and height. Flow these back + // so we can save some cycles working it out for ourselves later. + optional int32 width = 3; + optional int32 height = 4; + optional bool deprecated_blob_key_set = 102; +} + +message InputSettings { + enum ORIENTATION_CORRECTION_TYPE { + UNCHANGED_ORIENTATION = 0; + CORRECT_ORIENTATION = 1; + } + + // Default ENUM 0, UNCHANGED_ORIENTATION. + optional ORIENTATION_CORRECTION_TYPE correct_exif_orientation = 1; + optional bool parse_metadata = 2; // Per bool-type, default is false. + optional int32 transparent_substitution_rgb = 3; + optional bool deprecated_correct_exif_orientation_set = 101; + optional bool deprecated_transparent_substitution_rgb_set = 103; +} + +message OutputSettings { + enum MIME_TYPE { + PNG = 0; + JPEG = 1; + WEBP = 2; + } + + optional MIME_TYPE mime_type = 1; // Default ENUM 0, PNG. + optional int32 quality = 2; +} + +message ImagesTransformRequest { + required ImageData image = 1; + repeated Transform transform = 2; + required OutputSettings output = 3; + optional InputSettings input = 4; +} + +message ImagesTransformResponse { + required ImageData image = 1; + + // Here we always pass back the width and height of the source image. Other + // metadata returned only if parse_metadata is True in the request. The + // metadata is a JSON encoded string. + optional string source_metadata = 2; +} + +message CompositeImageOptions { + // Index of the image to use for this composition. + required int32 source_index = 1; + required int32 x_offset = 2; + required int32 y_offset = 3; + required float opacity = 4; + + // Places the anchor point of the image on the anchor point of the canvas + // before applying the offsets. + enum ANCHOR { + TOP_LEFT = 0; + TOP = 1; + TOP_RIGHT = 2; + LEFT = 3; + CENTER = 4; + RIGHT = 5; + BOTTOM_LEFT = 6; + BOTTOM = 7; + BOTTOM_RIGHT = 8; + } + + required ANCHOR anchor = 5; +} + +message ImagesCanvas { + required int32 width = 1; + required int32 height = 2; + required OutputSettings output = 3; + + // WARNING: Default is -1, must continue to be enforced in cc file. + // This default must be kept in this proto2 file as it affects the user + // facing libraries. + optional int32 color = 4 [default = -1]; // Default to opaque white. + + optional bool deprecated_color_set = 104; +} + +message ImagesCompositeRequest { + repeated ImageData image = 1; + repeated CompositeImageOptions options = 2; + required ImagesCanvas canvas = 3; +} + +message ImagesCompositeResponse { + required ImageData image = 1; +} + +message ImagesHistogramRequest { + required ImageData image = 1; +} + +message ImagesHistogram { + // 256 for each color channel. + repeated int32 red = 1; + repeated int32 green = 2; + repeated int32 blue = 3; +} + +message ImagesHistogramResponse { + required ImagesHistogram histogram = 1; +} + +message ImagesGetUrlBaseRequest { + // Obtaining an ImageUrl requires that the image is persisted in Blobstore. + required string blob_key = 1; + + // If true, the resulting URL should use https. + optional bool create_secure_url = 2; // Per bool-type, default is false. +} + +message ImagesGetUrlBaseResponse { + required string url = 1; +} + +message ImagesDeleteUrlBaseRequest { + // The blob key associated with the URL we wish to delete. + required string blob_key = 1; +} + +message ImagesDeleteUrlBaseResponse { + // Reserved for future use. +} diff --git a/src/google/appengine/api/images/images_service_pb2.py b/src/google/appengine/api/images/images_service_pb2.py index bc3e497..1a31ebe 100755 --- a/src/google/appengine/api/images/images_service_pb2.py +++ b/src/google/appengine/api/images/images_service_pb2.py @@ -1,248 +1,85 @@ -#!/usr/bin/env python -# -# Copyright 2007 Google LLC -# -# 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. -# - - - +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: images_service.proto +# Protobuf Python Version: 5.29.0 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database - +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 0, + '', + 'images_service.proto' +) +# @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n0google/appengine/api/images/images_service.proto\x12\x10google.appengine\"\xc8\x01\n\x12ImagesServiceError\"\xb1\x01\n\tErrorCode\x12\x15\n\x11UNSPECIFIED_ERROR\x10\x01\x12\x16\n\x12\x42\x41\x44_TRANSFORM_DATA\x10\x02\x12\r\n\tNOT_IMAGE\x10\x03\x12\x12\n\x0e\x42\x41\x44_IMAGE_DATA\x10\x04\x12\x13\n\x0fIMAGE_TOO_LARGE\x10\x05\x12\x14\n\x10INVALID_BLOB_KEY\x10\x06\x12\x11\n\rACCESS_DENIED\x10\x07\x12\x14\n\x10OBJECT_NOT_FOUND\x10\x08\"\x80\x01\n\x16ImagesServiceTransform\"f\n\x04Type\x12\n\n\x06RESIZE\x10\x01\x12\n\n\x06ROTATE\x10\x02\x12\x13\n\x0fHORIZONTAL_FLIP\x10\x03\x12\x11\n\rVERTICAL_FLIP\x10\x04\x12\x08\n\x04\x43ROP\x10\x05\x12\x14\n\x10IM_FEELING_LUCKY\x10\x06\"\x92\x04\n\tTransform\x12\r\n\x05width\x18\x01 \x01(\x05\x12\x0e\n\x06height\x18\x02 \x01(\x05\x12\x13\n\x0b\x63rop_to_fit\x18\x0b \x01(\x08\x12\x1a\n\rcrop_offset_x\x18\x0c \x01(\x02:\x03\x30.5\x12\x1a\n\rcrop_offset_y\x18\r \x01(\x02:\x03\x30.5\x12\x0e\n\x06rotate\x18\x03 \x01(\x05\x12\x17\n\x0fhorizontal_flip\x18\x04 \x01(\x08\x12\x15\n\rvertical_flip\x18\x05 \x01(\x08\x12\x13\n\x0b\x63rop_left_x\x18\x06 \x01(\x02\x12\x12\n\ncrop_top_y\x18\x07 \x01(\x02\x12\x17\n\x0c\x63rop_right_x\x18\x08 \x01(\x02:\x01\x31\x12\x18\n\rcrop_bottom_y\x18\t \x01(\x02:\x01\x31\x12\x12\n\nautolevels\x18\n \x01(\x08\x12\x15\n\rallow_stretch\x18\x0e \x01(\x08\x12\x1c\n\x14\x64\x65precated_width_set\x18\x65 \x01(\x08\x12\x1d\n\x15\x64\x65precated_height_set\x18\x66 \x01(\x08\x12$\n\x1c\x64\x65precated_crop_offset_x_set\x18p \x01(\x08\x12$\n\x1c\x64\x65precated_crop_offset_y_set\x18q \x01(\x08\x12#\n\x1b\x64\x65precated_crop_right_x_set\x18l \x01(\x08\x12$\n\x1c\x64\x65precated_crop_bottom_y_set\x18m \x01(\x08\"r\n\tImageData\x12\x13\n\x07\x63ontent\x18\x01 \x02(\x0c\x42\x02\x08\x01\x12\x10\n\x08\x62lob_key\x18\x02 \x01(\t\x12\r\n\x05width\x18\x03 \x01(\x05\x12\x0e\n\x06height\x18\x04 \x01(\x05\x12\x1f\n\x17\x64\x65precated_blob_key_set\x18\x66 \x01(\x08\"\xe5\x02\n\rInputSettings\x12]\n\x18\x63orrect_exif_orientation\x18\x01 \x01(\x0e\x32;.google.appengine.InputSettings.ORIENTATION_CORRECTION_TYPE\x12\x16\n\x0eparse_metadata\x18\x02 \x01(\x08\x12$\n\x1ctransparent_substitution_rgb\x18\x03 \x01(\x05\x12/\n\'deprecated_correct_exif_orientation_set\x18\x65 \x01(\x08\x12\x33\n+deprecated_transparent_substitution_rgb_set\x18g \x01(\x08\"Q\n\x1bORIENTATION_CORRECTION_TYPE\x12\x19\n\x15UNCHANGED_ORIENTATION\x10\x00\x12\x17\n\x13\x43ORRECT_ORIENTATION\x10\x01\"\x8a\x01\n\x0eOutputSettings\x12=\n\tmime_type\x18\x01 \x01(\x0e\x32*.google.appengine.OutputSettings.MIME_TYPE\x12\x0f\n\x07quality\x18\x02 \x01(\x05\"(\n\tMIME_TYPE\x12\x07\n\x03PNG\x10\x00\x12\x08\n\x04JPEG\x10\x01\x12\x08\n\x04WEBP\x10\x02\"\xd6\x01\n\x16ImagesTransformRequest\x12*\n\x05image\x18\x01 \x02(\x0b\x32\x1b.google.appengine.ImageData\x12.\n\ttransform\x18\x02 \x03(\x0b\x32\x1b.google.appengine.Transform\x12\x30\n\x06output\x18\x03 \x02(\x0b\x32 .google.appengine.OutputSettings\x12.\n\x05input\x18\x04 \x01(\x0b\x32\x1f.google.appengine.InputSettings\"^\n\x17ImagesTransformResponse\x12*\n\x05image\x18\x01 \x02(\x0b\x32\x1b.google.appengine.ImageData\x12\x17\n\x0fsource_metadata\x18\x02 \x01(\t\"\xa2\x02\n\x15\x43ompositeImageOptions\x12\x14\n\x0csource_index\x18\x01 \x02(\x05\x12\x10\n\x08x_offset\x18\x02 \x02(\x05\x12\x10\n\x08y_offset\x18\x03 \x02(\x05\x12\x0f\n\x07opacity\x18\x04 \x02(\x02\x12>\n\x06\x61nchor\x18\x05 \x02(\x0e\x32..google.appengine.CompositeImageOptions.ANCHOR\"~\n\x06\x41NCHOR\x12\x0c\n\x08TOP_LEFT\x10\x00\x12\x07\n\x03TOP\x10\x01\x12\r\n\tTOP_RIGHT\x10\x02\x12\x08\n\x04LEFT\x10\x03\x12\n\n\x06\x43\x45NTER\x10\x04\x12\t\n\x05RIGHT\x10\x05\x12\x0f\n\x0b\x42OTTOM_LEFT\x10\x06\x12\n\n\x06\x42OTTOM\x10\x07\x12\x10\n\x0c\x42OTTOM_RIGHT\x10\x08\"\x90\x01\n\x0cImagesCanvas\x12\r\n\x05width\x18\x01 \x02(\x05\x12\x0e\n\x06height\x18\x02 \x02(\x05\x12\x30\n\x06output\x18\x03 \x02(\x0b\x32 .google.appengine.OutputSettings\x12\x11\n\x05\x63olor\x18\x04 \x01(\x05:\x02-1\x12\x1c\n\x14\x64\x65precated_color_set\x18h \x01(\x08\"\xae\x01\n\x16ImagesCompositeRequest\x12*\n\x05image\x18\x01 \x03(\x0b\x32\x1b.google.appengine.ImageData\x12\x38\n\x07options\x18\x02 \x03(\x0b\x32\'.google.appengine.CompositeImageOptions\x12.\n\x06\x63\x61nvas\x18\x03 \x02(\x0b\x32\x1e.google.appengine.ImagesCanvas\"E\n\x17ImagesCompositeResponse\x12*\n\x05image\x18\x01 \x02(\x0b\x32\x1b.google.appengine.ImageData\"D\n\x16ImagesHistogramRequest\x12*\n\x05image\x18\x01 \x02(\x0b\x32\x1b.google.appengine.ImageData\";\n\x0fImagesHistogram\x12\x0b\n\x03red\x18\x01 \x03(\x05\x12\r\n\x05green\x18\x02 \x03(\x05\x12\x0c\n\x04\x62lue\x18\x03 \x03(\x05\"O\n\x17ImagesHistogramResponse\x12\x34\n\thistogram\x18\x01 \x02(\x0b\x32!.google.appengine.ImagesHistogram\"F\n\x17ImagesGetUrlBaseRequest\x12\x10\n\x08\x62lob_key\x18\x01 \x02(\t\x12\x19\n\x11\x63reate_secure_url\x18\x02 \x01(\x08\"\'\n\x18ImagesGetUrlBaseResponse\x12\x0b\n\x03url\x18\x01 \x02(\t\".\n\x1aImagesDeleteUrlBaseRequest\x12\x10\n\x08\x62lob_key\x18\x01 \x02(\t\"\x1d\n\x1bImagesDeleteUrlBaseResponseB5\n\x1f\x63om.google.appengine.api.imagesB\x0fImagesServicePb\x88\x01\x01') - - - -_IMAGESSERVICEERROR = DESCRIPTOR.message_types_by_name['ImagesServiceError'] -_IMAGESSERVICETRANSFORM = DESCRIPTOR.message_types_by_name['ImagesServiceTransform'] -_TRANSFORM = DESCRIPTOR.message_types_by_name['Transform'] -_IMAGEDATA = DESCRIPTOR.message_types_by_name['ImageData'] -_INPUTSETTINGS = DESCRIPTOR.message_types_by_name['InputSettings'] -_OUTPUTSETTINGS = DESCRIPTOR.message_types_by_name['OutputSettings'] -_IMAGESTRANSFORMREQUEST = DESCRIPTOR.message_types_by_name['ImagesTransformRequest'] -_IMAGESTRANSFORMRESPONSE = DESCRIPTOR.message_types_by_name['ImagesTransformResponse'] -_COMPOSITEIMAGEOPTIONS = DESCRIPTOR.message_types_by_name['CompositeImageOptions'] -_IMAGESCANVAS = DESCRIPTOR.message_types_by_name['ImagesCanvas'] -_IMAGESCOMPOSITEREQUEST = DESCRIPTOR.message_types_by_name['ImagesCompositeRequest'] -_IMAGESCOMPOSITERESPONSE = DESCRIPTOR.message_types_by_name['ImagesCompositeResponse'] -_IMAGESHISTOGRAMREQUEST = DESCRIPTOR.message_types_by_name['ImagesHistogramRequest'] -_IMAGESHISTOGRAM = DESCRIPTOR.message_types_by_name['ImagesHistogram'] -_IMAGESHISTOGRAMRESPONSE = DESCRIPTOR.message_types_by_name['ImagesHistogramResponse'] -_IMAGESGETURLBASEREQUEST = DESCRIPTOR.message_types_by_name['ImagesGetUrlBaseRequest'] -_IMAGESGETURLBASERESPONSE = DESCRIPTOR.message_types_by_name['ImagesGetUrlBaseResponse'] -_IMAGESDELETEURLBASEREQUEST = DESCRIPTOR.message_types_by_name['ImagesDeleteUrlBaseRequest'] -_IMAGESDELETEURLBASERESPONSE = DESCRIPTOR.message_types_by_name['ImagesDeleteUrlBaseResponse'] -_IMAGESSERVICEERROR_ERRORCODE = _IMAGESSERVICEERROR.enum_types_by_name['ErrorCode'] -_IMAGESSERVICETRANSFORM_TYPE = _IMAGESSERVICETRANSFORM.enum_types_by_name['Type'] -_INPUTSETTINGS_ORIENTATION_CORRECTION_TYPE = _INPUTSETTINGS.enum_types_by_name['ORIENTATION_CORRECTION_TYPE'] -_OUTPUTSETTINGS_MIME_TYPE = _OUTPUTSETTINGS.enum_types_by_name['MIME_TYPE'] -_COMPOSITEIMAGEOPTIONS_ANCHOR = _COMPOSITEIMAGEOPTIONS.enum_types_by_name['ANCHOR'] -ImagesServiceError = _reflection.GeneratedProtocolMessageType('ImagesServiceError', (_message.Message,), { - 'DESCRIPTOR' : _IMAGESSERVICEERROR, - '__module__' : 'google.appengine.api.images.images_service_pb2' - - }) -_sym_db.RegisterMessage(ImagesServiceError) - -ImagesServiceTransform = _reflection.GeneratedProtocolMessageType('ImagesServiceTransform', (_message.Message,), { - 'DESCRIPTOR' : _IMAGESSERVICETRANSFORM, - '__module__' : 'google.appengine.api.images.images_service_pb2' - - }) -_sym_db.RegisterMessage(ImagesServiceTransform) - -Transform = _reflection.GeneratedProtocolMessageType('Transform', (_message.Message,), { - 'DESCRIPTOR' : _TRANSFORM, - '__module__' : 'google.appengine.api.images.images_service_pb2' - - }) -_sym_db.RegisterMessage(Transform) - -ImageData = _reflection.GeneratedProtocolMessageType('ImageData', (_message.Message,), { - 'DESCRIPTOR' : _IMAGEDATA, - '__module__' : 'google.appengine.api.images.images_service_pb2' - - }) -_sym_db.RegisterMessage(ImageData) - -InputSettings = _reflection.GeneratedProtocolMessageType('InputSettings', (_message.Message,), { - 'DESCRIPTOR' : _INPUTSETTINGS, - '__module__' : 'google.appengine.api.images.images_service_pb2' - - }) -_sym_db.RegisterMessage(InputSettings) - -OutputSettings = _reflection.GeneratedProtocolMessageType('OutputSettings', (_message.Message,), { - 'DESCRIPTOR' : _OUTPUTSETTINGS, - '__module__' : 'google.appengine.api.images.images_service_pb2' - - }) -_sym_db.RegisterMessage(OutputSettings) - -ImagesTransformRequest = _reflection.GeneratedProtocolMessageType('ImagesTransformRequest', (_message.Message,), { - 'DESCRIPTOR' : _IMAGESTRANSFORMREQUEST, - '__module__' : 'google.appengine.api.images.images_service_pb2' - - }) -_sym_db.RegisterMessage(ImagesTransformRequest) - -ImagesTransformResponse = _reflection.GeneratedProtocolMessageType('ImagesTransformResponse', (_message.Message,), { - 'DESCRIPTOR' : _IMAGESTRANSFORMRESPONSE, - '__module__' : 'google.appengine.api.images.images_service_pb2' - - }) -_sym_db.RegisterMessage(ImagesTransformResponse) - -CompositeImageOptions = _reflection.GeneratedProtocolMessageType('CompositeImageOptions', (_message.Message,), { - 'DESCRIPTOR' : _COMPOSITEIMAGEOPTIONS, - '__module__' : 'google.appengine.api.images.images_service_pb2' - - }) -_sym_db.RegisterMessage(CompositeImageOptions) - -ImagesCanvas = _reflection.GeneratedProtocolMessageType('ImagesCanvas', (_message.Message,), { - 'DESCRIPTOR' : _IMAGESCANVAS, - '__module__' : 'google.appengine.api.images.images_service_pb2' - - }) -_sym_db.RegisterMessage(ImagesCanvas) - -ImagesCompositeRequest = _reflection.GeneratedProtocolMessageType('ImagesCompositeRequest', (_message.Message,), { - 'DESCRIPTOR' : _IMAGESCOMPOSITEREQUEST, - '__module__' : 'google.appengine.api.images.images_service_pb2' - - }) -_sym_db.RegisterMessage(ImagesCompositeRequest) - -ImagesCompositeResponse = _reflection.GeneratedProtocolMessageType('ImagesCompositeResponse', (_message.Message,), { - 'DESCRIPTOR' : _IMAGESCOMPOSITERESPONSE, - '__module__' : 'google.appengine.api.images.images_service_pb2' - - }) -_sym_db.RegisterMessage(ImagesCompositeResponse) - -ImagesHistogramRequest = _reflection.GeneratedProtocolMessageType('ImagesHistogramRequest', (_message.Message,), { - 'DESCRIPTOR' : _IMAGESHISTOGRAMREQUEST, - '__module__' : 'google.appengine.api.images.images_service_pb2' - - }) -_sym_db.RegisterMessage(ImagesHistogramRequest) - -ImagesHistogram = _reflection.GeneratedProtocolMessageType('ImagesHistogram', (_message.Message,), { - 'DESCRIPTOR' : _IMAGESHISTOGRAM, - '__module__' : 'google.appengine.api.images.images_service_pb2' - - }) -_sym_db.RegisterMessage(ImagesHistogram) - -ImagesHistogramResponse = _reflection.GeneratedProtocolMessageType('ImagesHistogramResponse', (_message.Message,), { - 'DESCRIPTOR' : _IMAGESHISTOGRAMRESPONSE, - '__module__' : 'google.appengine.api.images.images_service_pb2' - - }) -_sym_db.RegisterMessage(ImagesHistogramResponse) - -ImagesGetUrlBaseRequest = _reflection.GeneratedProtocolMessageType('ImagesGetUrlBaseRequest', (_message.Message,), { - 'DESCRIPTOR' : _IMAGESGETURLBASEREQUEST, - '__module__' : 'google.appengine.api.images.images_service_pb2' - - }) -_sym_db.RegisterMessage(ImagesGetUrlBaseRequest) - -ImagesGetUrlBaseResponse = _reflection.GeneratedProtocolMessageType('ImagesGetUrlBaseResponse', (_message.Message,), { - 'DESCRIPTOR' : _IMAGESGETURLBASERESPONSE, - '__module__' : 'google.appengine.api.images.images_service_pb2' - - }) -_sym_db.RegisterMessage(ImagesGetUrlBaseResponse) - -ImagesDeleteUrlBaseRequest = _reflection.GeneratedProtocolMessageType('ImagesDeleteUrlBaseRequest', (_message.Message,), { - 'DESCRIPTOR' : _IMAGESDELETEURLBASEREQUEST, - '__module__' : 'google.appengine.api.images.images_service_pb2' - - }) -_sym_db.RegisterMessage(ImagesDeleteUrlBaseRequest) - -ImagesDeleteUrlBaseResponse = _reflection.GeneratedProtocolMessageType('ImagesDeleteUrlBaseResponse', (_message.Message,), { - 'DESCRIPTOR' : _IMAGESDELETEURLBASERESPONSE, - '__module__' : 'google.appengine.api.images.images_service_pb2' - - }) -_sym_db.RegisterMessage(ImagesDeleteUrlBaseResponse) - -if _descriptor._USE_C_DESCRIPTORS == False: - - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\037com.google.appengine.api.imagesB\017ImagesServicePb\210\001\001' - _IMAGEDATA.fields_by_name['content']._options = None - _IMAGEDATA.fields_by_name['content']._serialized_options = b'\010\001' - _IMAGESSERVICEERROR._serialized_start=71 - _IMAGESSERVICEERROR._serialized_end=271 - _IMAGESSERVICEERROR_ERRORCODE._serialized_start=94 - _IMAGESSERVICEERROR_ERRORCODE._serialized_end=271 - _IMAGESSERVICETRANSFORM._serialized_start=274 - _IMAGESSERVICETRANSFORM._serialized_end=402 - _IMAGESSERVICETRANSFORM_TYPE._serialized_start=300 - _IMAGESSERVICETRANSFORM_TYPE._serialized_end=402 - _TRANSFORM._serialized_start=405 - _TRANSFORM._serialized_end=935 - _IMAGEDATA._serialized_start=937 - _IMAGEDATA._serialized_end=1051 - _INPUTSETTINGS._serialized_start=1054 - _INPUTSETTINGS._serialized_end=1411 - _INPUTSETTINGS_ORIENTATION_CORRECTION_TYPE._serialized_start=1330 - _INPUTSETTINGS_ORIENTATION_CORRECTION_TYPE._serialized_end=1411 - _OUTPUTSETTINGS._serialized_start=1414 - _OUTPUTSETTINGS._serialized_end=1552 - _OUTPUTSETTINGS_MIME_TYPE._serialized_start=1512 - _OUTPUTSETTINGS_MIME_TYPE._serialized_end=1552 - _IMAGESTRANSFORMREQUEST._serialized_start=1555 - _IMAGESTRANSFORMREQUEST._serialized_end=1769 - _IMAGESTRANSFORMRESPONSE._serialized_start=1771 - _IMAGESTRANSFORMRESPONSE._serialized_end=1865 - _COMPOSITEIMAGEOPTIONS._serialized_start=1868 - _COMPOSITEIMAGEOPTIONS._serialized_end=2158 - _COMPOSITEIMAGEOPTIONS_ANCHOR._serialized_start=2032 - _COMPOSITEIMAGEOPTIONS_ANCHOR._serialized_end=2158 - _IMAGESCANVAS._serialized_start=2161 - _IMAGESCANVAS._serialized_end=2305 - _IMAGESCOMPOSITEREQUEST._serialized_start=2308 - _IMAGESCOMPOSITEREQUEST._serialized_end=2482 - _IMAGESCOMPOSITERESPONSE._serialized_start=2484 - _IMAGESCOMPOSITERESPONSE._serialized_end=2553 - _IMAGESHISTOGRAMREQUEST._serialized_start=2555 - _IMAGESHISTOGRAMREQUEST._serialized_end=2623 - _IMAGESHISTOGRAM._serialized_start=2625 - _IMAGESHISTOGRAM._serialized_end=2684 - _IMAGESHISTOGRAMRESPONSE._serialized_start=2686 - _IMAGESHISTOGRAMRESPONSE._serialized_end=2765 - _IMAGESGETURLBASEREQUEST._serialized_start=2767 - _IMAGESGETURLBASEREQUEST._serialized_end=2837 - _IMAGESGETURLBASERESPONSE._serialized_start=2839 - _IMAGESGETURLBASERESPONSE._serialized_end=2878 - _IMAGESDELETEURLBASEREQUEST._serialized_start=2880 - _IMAGESDELETEURLBASEREQUEST._serialized_end=2926 - _IMAGESDELETEURLBASERESPONSE._serialized_start=2928 - _IMAGESDELETEURLBASERESPONSE._serialized_end=2957 - +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14images_service.proto\x12\napphosting\"\xc8\x01\n\x12ImagesServiceError\"\xb1\x01\n\tErrorCode\x12\x15\n\x11UNSPECIFIED_ERROR\x10\x01\x12\x16\n\x12\x42\x41\x44_TRANSFORM_DATA\x10\x02\x12\r\n\tNOT_IMAGE\x10\x03\x12\x12\n\x0e\x42\x41\x44_IMAGE_DATA\x10\x04\x12\x13\n\x0fIMAGE_TOO_LARGE\x10\x05\x12\x14\n\x10INVALID_BLOB_KEY\x10\x06\x12\x11\n\rACCESS_DENIED\x10\x07\x12\x14\n\x10OBJECT_NOT_FOUND\x10\x08\"\x80\x01\n\x16ImagesServiceTransform\"f\n\x04Type\x12\n\n\x06RESIZE\x10\x01\x12\n\n\x06ROTATE\x10\x02\x12\x13\n\x0fHORIZONTAL_FLIP\x10\x03\x12\x11\n\rVERTICAL_FLIP\x10\x04\x12\x08\n\x04\x43ROP\x10\x05\x12\x14\n\x10IM_FEELING_LUCKY\x10\x06\"\x92\x04\n\tTransform\x12\r\n\x05width\x18\x01 \x01(\x05\x12\x0e\n\x06height\x18\x02 \x01(\x05\x12\x13\n\x0b\x63rop_to_fit\x18\x0b \x01(\x08\x12\x1a\n\rcrop_offset_x\x18\x0c \x01(\x02:\x03\x30.5\x12\x1a\n\rcrop_offset_y\x18\r \x01(\x02:\x03\x30.5\x12\x0e\n\x06rotate\x18\x03 \x01(\x05\x12\x17\n\x0fhorizontal_flip\x18\x04 \x01(\x08\x12\x15\n\rvertical_flip\x18\x05 \x01(\x08\x12\x13\n\x0b\x63rop_left_x\x18\x06 \x01(\x02\x12\x12\n\ncrop_top_y\x18\x07 \x01(\x02\x12\x17\n\x0c\x63rop_right_x\x18\x08 \x01(\x02:\x01\x31\x12\x18\n\rcrop_bottom_y\x18\t \x01(\x02:\x01\x31\x12\x12\n\nautolevels\x18\n \x01(\x08\x12\x15\n\rallow_stretch\x18\x0e \x01(\x08\x12\x1c\n\x14\x64\x65precated_width_set\x18\x65 \x01(\x08\x12\x1d\n\x15\x64\x65precated_height_set\x18\x66 \x01(\x08\x12$\n\x1c\x64\x65precated_crop_offset_x_set\x18p \x01(\x08\x12$\n\x1c\x64\x65precated_crop_offset_y_set\x18q \x01(\x08\x12#\n\x1b\x64\x65precated_crop_right_x_set\x18l \x01(\x08\x12$\n\x1c\x64\x65precated_crop_bottom_y_set\x18m \x01(\x08\"r\n\tImageData\x12\x13\n\x07\x63ontent\x18\x01 \x02(\x0c\x42\x02\x08\x01\x12\x10\n\x08\x62lob_key\x18\x02 \x01(\t\x12\r\n\x05width\x18\x03 \x01(\x05\x12\x0e\n\x06height\x18\x04 \x01(\x05\x12\x1f\n\x17\x64\x65precated_blob_key_set\x18\x66 \x01(\x08\"\xdf\x02\n\rInputSettings\x12W\n\x18\x63orrect_exif_orientation\x18\x01 \x01(\x0e\x32\x35.apphosting.InputSettings.ORIENTATION_CORRECTION_TYPE\x12\x16\n\x0eparse_metadata\x18\x02 \x01(\x08\x12$\n\x1ctransparent_substitution_rgb\x18\x03 \x01(\x05\x12/\n\'deprecated_correct_exif_orientation_set\x18\x65 \x01(\x08\x12\x33\n+deprecated_transparent_substitution_rgb_set\x18g \x01(\x08\"Q\n\x1bORIENTATION_CORRECTION_TYPE\x12\x19\n\x15UNCHANGED_ORIENTATION\x10\x00\x12\x17\n\x13\x43ORRECT_ORIENTATION\x10\x01\"\x84\x01\n\x0eOutputSettings\x12\x37\n\tmime_type\x18\x01 \x01(\x0e\x32$.apphosting.OutputSettings.MIME_TYPE\x12\x0f\n\x07quality\x18\x02 \x01(\x05\"(\n\tMIME_TYPE\x12\x07\n\x03PNG\x10\x00\x12\x08\n\x04JPEG\x10\x01\x12\x08\n\x04WEBP\x10\x02\"\xbe\x01\n\x16ImagesTransformRequest\x12$\n\x05image\x18\x01 \x02(\x0b\x32\x15.apphosting.ImageData\x12(\n\ttransform\x18\x02 \x03(\x0b\x32\x15.apphosting.Transform\x12*\n\x06output\x18\x03 \x02(\x0b\x32\x1a.apphosting.OutputSettings\x12(\n\x05input\x18\x04 \x01(\x0b\x32\x19.apphosting.InputSettings\"X\n\x17ImagesTransformResponse\x12$\n\x05image\x18\x01 \x02(\x0b\x32\x15.apphosting.ImageData\x12\x17\n\x0fsource_metadata\x18\x02 \x01(\t\"\x9c\x02\n\x15\x43ompositeImageOptions\x12\x14\n\x0csource_index\x18\x01 \x02(\x05\x12\x10\n\x08x_offset\x18\x02 \x02(\x05\x12\x10\n\x08y_offset\x18\x03 \x02(\x05\x12\x0f\n\x07opacity\x18\x04 \x02(\x02\x12\x38\n\x06\x61nchor\x18\x05 \x02(\x0e\x32(.apphosting.CompositeImageOptions.ANCHOR\"~\n\x06\x41NCHOR\x12\x0c\n\x08TOP_LEFT\x10\x00\x12\x07\n\x03TOP\x10\x01\x12\r\n\tTOP_RIGHT\x10\x02\x12\x08\n\x04LEFT\x10\x03\x12\n\n\x06\x43\x45NTER\x10\x04\x12\t\n\x05RIGHT\x10\x05\x12\x0f\n\x0b\x42OTTOM_LEFT\x10\x06\x12\n\n\x06\x42OTTOM\x10\x07\x12\x10\n\x0c\x42OTTOM_RIGHT\x10\x08\"\x8a\x01\n\x0cImagesCanvas\x12\r\n\x05width\x18\x01 \x02(\x05\x12\x0e\n\x06height\x18\x02 \x02(\x05\x12*\n\x06output\x18\x03 \x02(\x0b\x32\x1a.apphosting.OutputSettings\x12\x11\n\x05\x63olor\x18\x04 \x01(\x05:\x02-1\x12\x1c\n\x14\x64\x65precated_color_set\x18h \x01(\x08\"\x9c\x01\n\x16ImagesCompositeRequest\x12$\n\x05image\x18\x01 \x03(\x0b\x32\x15.apphosting.ImageData\x12\x32\n\x07options\x18\x02 \x03(\x0b\x32!.apphosting.CompositeImageOptions\x12(\n\x06\x63\x61nvas\x18\x03 \x02(\x0b\x32\x18.apphosting.ImagesCanvas\"?\n\x17ImagesCompositeResponse\x12$\n\x05image\x18\x01 \x02(\x0b\x32\x15.apphosting.ImageData\">\n\x16ImagesHistogramRequest\x12$\n\x05image\x18\x01 \x02(\x0b\x32\x15.apphosting.ImageData\";\n\x0fImagesHistogram\x12\x0b\n\x03red\x18\x01 \x03(\x05\x12\r\n\x05green\x18\x02 \x03(\x05\x12\x0c\n\x04\x62lue\x18\x03 \x03(\x05\"I\n\x17ImagesHistogramResponse\x12.\n\thistogram\x18\x01 \x02(\x0b\x32\x1b.apphosting.ImagesHistogram\"F\n\x17ImagesGetUrlBaseRequest\x12\x10\n\x08\x62lob_key\x18\x01 \x02(\t\x12\x19\n\x11\x63reate_secure_url\x18\x02 \x01(\x08\"\'\n\x18ImagesGetUrlBaseResponse\x12\x0b\n\x03url\x18\x01 \x02(\t\".\n\x1aImagesDeleteUrlBaseRequest\x12\x10\n\x08\x62lob_key\x18\x01 \x02(\t\"\x1d\n\x1bImagesDeleteUrlBaseResponseB5\n\x1f\x63om.google.appengine.api.imagesB\x0fImagesServicePb\x88\x01\x01') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'images_service_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\037com.google.appengine.api.imagesB\017ImagesServicePb\210\001\001' + _globals['_IMAGEDATA'].fields_by_name['content']._loaded_options = None + _globals['_IMAGEDATA'].fields_by_name['content']._serialized_options = b'\010\001' + _globals['_IMAGESSERVICEERROR']._serialized_start=37 + _globals['_IMAGESSERVICEERROR']._serialized_end=237 + _globals['_IMAGESSERVICEERROR_ERRORCODE']._serialized_start=60 + _globals['_IMAGESSERVICEERROR_ERRORCODE']._serialized_end=237 + _globals['_IMAGESSERVICETRANSFORM']._serialized_start=240 + _globals['_IMAGESSERVICETRANSFORM']._serialized_end=368 + _globals['_IMAGESSERVICETRANSFORM_TYPE']._serialized_start=266 + _globals['_IMAGESSERVICETRANSFORM_TYPE']._serialized_end=368 + _globals['_TRANSFORM']._serialized_start=371 + _globals['_TRANSFORM']._serialized_end=901 + _globals['_IMAGEDATA']._serialized_start=903 + _globals['_IMAGEDATA']._serialized_end=1017 + _globals['_INPUTSETTINGS']._serialized_start=1020 + _globals['_INPUTSETTINGS']._serialized_end=1371 + _globals['_INPUTSETTINGS_ORIENTATION_CORRECTION_TYPE']._serialized_start=1290 + _globals['_INPUTSETTINGS_ORIENTATION_CORRECTION_TYPE']._serialized_end=1371 + _globals['_OUTPUTSETTINGS']._serialized_start=1374 + _globals['_OUTPUTSETTINGS']._serialized_end=1506 + _globals['_OUTPUTSETTINGS_MIME_TYPE']._serialized_start=1466 + _globals['_OUTPUTSETTINGS_MIME_TYPE']._serialized_end=1506 + _globals['_IMAGESTRANSFORMREQUEST']._serialized_start=1509 + _globals['_IMAGESTRANSFORMREQUEST']._serialized_end=1699 + _globals['_IMAGESTRANSFORMRESPONSE']._serialized_start=1701 + _globals['_IMAGESTRANSFORMRESPONSE']._serialized_end=1789 + _globals['_COMPOSITEIMAGEOPTIONS']._serialized_start=1792 + _globals['_COMPOSITEIMAGEOPTIONS']._serialized_end=2076 + _globals['_COMPOSITEIMAGEOPTIONS_ANCHOR']._serialized_start=1950 + _globals['_COMPOSITEIMAGEOPTIONS_ANCHOR']._serialized_end=2076 + _globals['_IMAGESCANVAS']._serialized_start=2079 + _globals['_IMAGESCANVAS']._serialized_end=2217 + _globals['_IMAGESCOMPOSITEREQUEST']._serialized_start=2220 + _globals['_IMAGESCOMPOSITEREQUEST']._serialized_end=2376 + _globals['_IMAGESCOMPOSITERESPONSE']._serialized_start=2378 + _globals['_IMAGESCOMPOSITERESPONSE']._serialized_end=2441 + _globals['_IMAGESHISTOGRAMREQUEST']._serialized_start=2443 + _globals['_IMAGESHISTOGRAMREQUEST']._serialized_end=2505 + _globals['_IMAGESHISTOGRAM']._serialized_start=2507 + _globals['_IMAGESHISTOGRAM']._serialized_end=2566 + _globals['_IMAGESHISTOGRAMRESPONSE']._serialized_start=2568 + _globals['_IMAGESHISTOGRAMRESPONSE']._serialized_end=2641 + _globals['_IMAGESGETURLBASEREQUEST']._serialized_start=2643 + _globals['_IMAGESGETURLBASEREQUEST']._serialized_end=2713 + _globals['_IMAGESGETURLBASERESPONSE']._serialized_start=2715 + _globals['_IMAGESGETURLBASERESPONSE']._serialized_end=2754 + _globals['_IMAGESDELETEURLBASEREQUEST']._serialized_start=2756 + _globals['_IMAGESDELETEURLBASEREQUEST']._serialized_end=2802 + _globals['_IMAGESDELETEURLBASERESPONSE']._serialized_start=2804 + _globals['_IMAGESDELETEURLBASERESPONSE']._serialized_end=2833 +# @@protoc_insertion_point(module_scope) diff --git a/src/google/appengine/api/images/images_service_pb2.pyi b/src/google/appengine/api/images/images_service_pb2.pyi new file mode 100644 index 0000000..563f2b5 --- /dev/null +++ b/src/google/appengine/api/images/images_service_pb2.pyi @@ -0,0 +1,271 @@ +from google.protobuf.internal import containers as _containers +from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class ImagesServiceError(_message.Message): + __slots__ = () + class ErrorCode(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + UNSPECIFIED_ERROR: _ClassVar[ImagesServiceError.ErrorCode] + BAD_TRANSFORM_DATA: _ClassVar[ImagesServiceError.ErrorCode] + NOT_IMAGE: _ClassVar[ImagesServiceError.ErrorCode] + BAD_IMAGE_DATA: _ClassVar[ImagesServiceError.ErrorCode] + IMAGE_TOO_LARGE: _ClassVar[ImagesServiceError.ErrorCode] + INVALID_BLOB_KEY: _ClassVar[ImagesServiceError.ErrorCode] + ACCESS_DENIED: _ClassVar[ImagesServiceError.ErrorCode] + OBJECT_NOT_FOUND: _ClassVar[ImagesServiceError.ErrorCode] + UNSPECIFIED_ERROR: ImagesServiceError.ErrorCode + BAD_TRANSFORM_DATA: ImagesServiceError.ErrorCode + NOT_IMAGE: ImagesServiceError.ErrorCode + BAD_IMAGE_DATA: ImagesServiceError.ErrorCode + IMAGE_TOO_LARGE: ImagesServiceError.ErrorCode + INVALID_BLOB_KEY: ImagesServiceError.ErrorCode + ACCESS_DENIED: ImagesServiceError.ErrorCode + OBJECT_NOT_FOUND: ImagesServiceError.ErrorCode + def __init__(self) -> None: ... + +class ImagesServiceTransform(_message.Message): + __slots__ = () + class Type(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + RESIZE: _ClassVar[ImagesServiceTransform.Type] + ROTATE: _ClassVar[ImagesServiceTransform.Type] + HORIZONTAL_FLIP: _ClassVar[ImagesServiceTransform.Type] + VERTICAL_FLIP: _ClassVar[ImagesServiceTransform.Type] + CROP: _ClassVar[ImagesServiceTransform.Type] + IM_FEELING_LUCKY: _ClassVar[ImagesServiceTransform.Type] + RESIZE: ImagesServiceTransform.Type + ROTATE: ImagesServiceTransform.Type + HORIZONTAL_FLIP: ImagesServiceTransform.Type + VERTICAL_FLIP: ImagesServiceTransform.Type + CROP: ImagesServiceTransform.Type + IM_FEELING_LUCKY: ImagesServiceTransform.Type + def __init__(self) -> None: ... + +class Transform(_message.Message): + __slots__ = ("width", "height", "crop_to_fit", "crop_offset_x", "crop_offset_y", "rotate", "horizontal_flip", "vertical_flip", "crop_left_x", "crop_top_y", "crop_right_x", "crop_bottom_y", "autolevels", "allow_stretch", "deprecated_width_set", "deprecated_height_set", "deprecated_crop_offset_x_set", "deprecated_crop_offset_y_set", "deprecated_crop_right_x_set", "deprecated_crop_bottom_y_set") + WIDTH_FIELD_NUMBER: _ClassVar[int] + HEIGHT_FIELD_NUMBER: _ClassVar[int] + CROP_TO_FIT_FIELD_NUMBER: _ClassVar[int] + CROP_OFFSET_X_FIELD_NUMBER: _ClassVar[int] + CROP_OFFSET_Y_FIELD_NUMBER: _ClassVar[int] + ROTATE_FIELD_NUMBER: _ClassVar[int] + HORIZONTAL_FLIP_FIELD_NUMBER: _ClassVar[int] + VERTICAL_FLIP_FIELD_NUMBER: _ClassVar[int] + CROP_LEFT_X_FIELD_NUMBER: _ClassVar[int] + CROP_TOP_Y_FIELD_NUMBER: _ClassVar[int] + CROP_RIGHT_X_FIELD_NUMBER: _ClassVar[int] + CROP_BOTTOM_Y_FIELD_NUMBER: _ClassVar[int] + AUTOLEVELS_FIELD_NUMBER: _ClassVar[int] + ALLOW_STRETCH_FIELD_NUMBER: _ClassVar[int] + DEPRECATED_WIDTH_SET_FIELD_NUMBER: _ClassVar[int] + DEPRECATED_HEIGHT_SET_FIELD_NUMBER: _ClassVar[int] + DEPRECATED_CROP_OFFSET_X_SET_FIELD_NUMBER: _ClassVar[int] + DEPRECATED_CROP_OFFSET_Y_SET_FIELD_NUMBER: _ClassVar[int] + DEPRECATED_CROP_RIGHT_X_SET_FIELD_NUMBER: _ClassVar[int] + DEPRECATED_CROP_BOTTOM_Y_SET_FIELD_NUMBER: _ClassVar[int] + width: int + height: int + crop_to_fit: bool + crop_offset_x: float + crop_offset_y: float + rotate: int + horizontal_flip: bool + vertical_flip: bool + crop_left_x: float + crop_top_y: float + crop_right_x: float + crop_bottom_y: float + autolevels: bool + allow_stretch: bool + deprecated_width_set: bool + deprecated_height_set: bool + deprecated_crop_offset_x_set: bool + deprecated_crop_offset_y_set: bool + deprecated_crop_right_x_set: bool + deprecated_crop_bottom_y_set: bool + def __init__(self, width: _Optional[int] = ..., height: _Optional[int] = ..., crop_to_fit: bool = ..., crop_offset_x: _Optional[float] = ..., crop_offset_y: _Optional[float] = ..., rotate: _Optional[int] = ..., horizontal_flip: bool = ..., vertical_flip: bool = ..., crop_left_x: _Optional[float] = ..., crop_top_y: _Optional[float] = ..., crop_right_x: _Optional[float] = ..., crop_bottom_y: _Optional[float] = ..., autolevels: bool = ..., allow_stretch: bool = ..., deprecated_width_set: bool = ..., deprecated_height_set: bool = ..., deprecated_crop_offset_x_set: bool = ..., deprecated_crop_offset_y_set: bool = ..., deprecated_crop_right_x_set: bool = ..., deprecated_crop_bottom_y_set: bool = ...) -> None: ... + +class ImageData(_message.Message): + __slots__ = ("content", "blob_key", "width", "height", "deprecated_blob_key_set") + CONTENT_FIELD_NUMBER: _ClassVar[int] + BLOB_KEY_FIELD_NUMBER: _ClassVar[int] + WIDTH_FIELD_NUMBER: _ClassVar[int] + HEIGHT_FIELD_NUMBER: _ClassVar[int] + DEPRECATED_BLOB_KEY_SET_FIELD_NUMBER: _ClassVar[int] + content: bytes + blob_key: str + width: int + height: int + deprecated_blob_key_set: bool + def __init__(self, content: _Optional[bytes] = ..., blob_key: _Optional[str] = ..., width: _Optional[int] = ..., height: _Optional[int] = ..., deprecated_blob_key_set: bool = ...) -> None: ... + +class InputSettings(_message.Message): + __slots__ = ("correct_exif_orientation", "parse_metadata", "transparent_substitution_rgb", "deprecated_correct_exif_orientation_set", "deprecated_transparent_substitution_rgb_set") + class ORIENTATION_CORRECTION_TYPE(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + UNCHANGED_ORIENTATION: _ClassVar[InputSettings.ORIENTATION_CORRECTION_TYPE] + CORRECT_ORIENTATION: _ClassVar[InputSettings.ORIENTATION_CORRECTION_TYPE] + UNCHANGED_ORIENTATION: InputSettings.ORIENTATION_CORRECTION_TYPE + CORRECT_ORIENTATION: InputSettings.ORIENTATION_CORRECTION_TYPE + CORRECT_EXIF_ORIENTATION_FIELD_NUMBER: _ClassVar[int] + PARSE_METADATA_FIELD_NUMBER: _ClassVar[int] + TRANSPARENT_SUBSTITUTION_RGB_FIELD_NUMBER: _ClassVar[int] + DEPRECATED_CORRECT_EXIF_ORIENTATION_SET_FIELD_NUMBER: _ClassVar[int] + DEPRECATED_TRANSPARENT_SUBSTITUTION_RGB_SET_FIELD_NUMBER: _ClassVar[int] + correct_exif_orientation: InputSettings.ORIENTATION_CORRECTION_TYPE + parse_metadata: bool + transparent_substitution_rgb: int + deprecated_correct_exif_orientation_set: bool + deprecated_transparent_substitution_rgb_set: bool + def __init__(self, correct_exif_orientation: _Optional[_Union[InputSettings.ORIENTATION_CORRECTION_TYPE, str]] = ..., parse_metadata: bool = ..., transparent_substitution_rgb: _Optional[int] = ..., deprecated_correct_exif_orientation_set: bool = ..., deprecated_transparent_substitution_rgb_set: bool = ...) -> None: ... + +class OutputSettings(_message.Message): + __slots__ = ("mime_type", "quality") + class MIME_TYPE(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + PNG: _ClassVar[OutputSettings.MIME_TYPE] + JPEG: _ClassVar[OutputSettings.MIME_TYPE] + WEBP: _ClassVar[OutputSettings.MIME_TYPE] + PNG: OutputSettings.MIME_TYPE + JPEG: OutputSettings.MIME_TYPE + WEBP: OutputSettings.MIME_TYPE + MIME_TYPE_FIELD_NUMBER: _ClassVar[int] + QUALITY_FIELD_NUMBER: _ClassVar[int] + mime_type: OutputSettings.MIME_TYPE + quality: int + def __init__(self, mime_type: _Optional[_Union[OutputSettings.MIME_TYPE, str]] = ..., quality: _Optional[int] = ...) -> None: ... + +class ImagesTransformRequest(_message.Message): + __slots__ = ("image", "transform", "output", "input") + IMAGE_FIELD_NUMBER: _ClassVar[int] + TRANSFORM_FIELD_NUMBER: _ClassVar[int] + OUTPUT_FIELD_NUMBER: _ClassVar[int] + INPUT_FIELD_NUMBER: _ClassVar[int] + image: ImageData + transform: _containers.RepeatedCompositeFieldContainer[Transform] + output: OutputSettings + input: InputSettings + def __init__(self, image: _Optional[_Union[ImageData, _Mapping]] = ..., transform: _Optional[_Iterable[_Union[Transform, _Mapping]]] = ..., output: _Optional[_Union[OutputSettings, _Mapping]] = ..., input: _Optional[_Union[InputSettings, _Mapping]] = ...) -> None: ... + +class ImagesTransformResponse(_message.Message): + __slots__ = ("image", "source_metadata") + IMAGE_FIELD_NUMBER: _ClassVar[int] + SOURCE_METADATA_FIELD_NUMBER: _ClassVar[int] + image: ImageData + source_metadata: str + def __init__(self, image: _Optional[_Union[ImageData, _Mapping]] = ..., source_metadata: _Optional[str] = ...) -> None: ... + +class CompositeImageOptions(_message.Message): + __slots__ = ("source_index", "x_offset", "y_offset", "opacity", "anchor") + class ANCHOR(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + TOP_LEFT: _ClassVar[CompositeImageOptions.ANCHOR] + TOP: _ClassVar[CompositeImageOptions.ANCHOR] + TOP_RIGHT: _ClassVar[CompositeImageOptions.ANCHOR] + LEFT: _ClassVar[CompositeImageOptions.ANCHOR] + CENTER: _ClassVar[CompositeImageOptions.ANCHOR] + RIGHT: _ClassVar[CompositeImageOptions.ANCHOR] + BOTTOM_LEFT: _ClassVar[CompositeImageOptions.ANCHOR] + BOTTOM: _ClassVar[CompositeImageOptions.ANCHOR] + BOTTOM_RIGHT: _ClassVar[CompositeImageOptions.ANCHOR] + TOP_LEFT: CompositeImageOptions.ANCHOR + TOP: CompositeImageOptions.ANCHOR + TOP_RIGHT: CompositeImageOptions.ANCHOR + LEFT: CompositeImageOptions.ANCHOR + CENTER: CompositeImageOptions.ANCHOR + RIGHT: CompositeImageOptions.ANCHOR + BOTTOM_LEFT: CompositeImageOptions.ANCHOR + BOTTOM: CompositeImageOptions.ANCHOR + BOTTOM_RIGHT: CompositeImageOptions.ANCHOR + SOURCE_INDEX_FIELD_NUMBER: _ClassVar[int] + X_OFFSET_FIELD_NUMBER: _ClassVar[int] + Y_OFFSET_FIELD_NUMBER: _ClassVar[int] + OPACITY_FIELD_NUMBER: _ClassVar[int] + ANCHOR_FIELD_NUMBER: _ClassVar[int] + source_index: int + x_offset: int + y_offset: int + opacity: float + anchor: CompositeImageOptions.ANCHOR + def __init__(self, source_index: _Optional[int] = ..., x_offset: _Optional[int] = ..., y_offset: _Optional[int] = ..., opacity: _Optional[float] = ..., anchor: _Optional[_Union[CompositeImageOptions.ANCHOR, str]] = ...) -> None: ... + +class ImagesCanvas(_message.Message): + __slots__ = ("width", "height", "output", "color", "deprecated_color_set") + WIDTH_FIELD_NUMBER: _ClassVar[int] + HEIGHT_FIELD_NUMBER: _ClassVar[int] + OUTPUT_FIELD_NUMBER: _ClassVar[int] + COLOR_FIELD_NUMBER: _ClassVar[int] + DEPRECATED_COLOR_SET_FIELD_NUMBER: _ClassVar[int] + width: int + height: int + output: OutputSettings + color: int + deprecated_color_set: bool + def __init__(self, width: _Optional[int] = ..., height: _Optional[int] = ..., output: _Optional[_Union[OutputSettings, _Mapping]] = ..., color: _Optional[int] = ..., deprecated_color_set: bool = ...) -> None: ... + +class ImagesCompositeRequest(_message.Message): + __slots__ = ("image", "options", "canvas") + IMAGE_FIELD_NUMBER: _ClassVar[int] + OPTIONS_FIELD_NUMBER: _ClassVar[int] + CANVAS_FIELD_NUMBER: _ClassVar[int] + image: _containers.RepeatedCompositeFieldContainer[ImageData] + options: _containers.RepeatedCompositeFieldContainer[CompositeImageOptions] + canvas: ImagesCanvas + def __init__(self, image: _Optional[_Iterable[_Union[ImageData, _Mapping]]] = ..., options: _Optional[_Iterable[_Union[CompositeImageOptions, _Mapping]]] = ..., canvas: _Optional[_Union[ImagesCanvas, _Mapping]] = ...) -> None: ... + +class ImagesCompositeResponse(_message.Message): + __slots__ = ("image",) + IMAGE_FIELD_NUMBER: _ClassVar[int] + image: ImageData + def __init__(self, image: _Optional[_Union[ImageData, _Mapping]] = ...) -> None: ... + +class ImagesHistogramRequest(_message.Message): + __slots__ = ("image",) + IMAGE_FIELD_NUMBER: _ClassVar[int] + image: ImageData + def __init__(self, image: _Optional[_Union[ImageData, _Mapping]] = ...) -> None: ... + +class ImagesHistogram(_message.Message): + __slots__ = ("red", "green", "blue") + RED_FIELD_NUMBER: _ClassVar[int] + GREEN_FIELD_NUMBER: _ClassVar[int] + BLUE_FIELD_NUMBER: _ClassVar[int] + red: _containers.RepeatedScalarFieldContainer[int] + green: _containers.RepeatedScalarFieldContainer[int] + blue: _containers.RepeatedScalarFieldContainer[int] + def __init__(self, red: _Optional[_Iterable[int]] = ..., green: _Optional[_Iterable[int]] = ..., blue: _Optional[_Iterable[int]] = ...) -> None: ... + +class ImagesHistogramResponse(_message.Message): + __slots__ = ("histogram",) + HISTOGRAM_FIELD_NUMBER: _ClassVar[int] + histogram: ImagesHistogram + def __init__(self, histogram: _Optional[_Union[ImagesHistogram, _Mapping]] = ...) -> None: ... + +class ImagesGetUrlBaseRequest(_message.Message): + __slots__ = ("blob_key", "create_secure_url") + BLOB_KEY_FIELD_NUMBER: _ClassVar[int] + CREATE_SECURE_URL_FIELD_NUMBER: _ClassVar[int] + blob_key: str + create_secure_url: bool + def __init__(self, blob_key: _Optional[str] = ..., create_secure_url: bool = ...) -> None: ... + +class ImagesGetUrlBaseResponse(_message.Message): + __slots__ = ("url",) + URL_FIELD_NUMBER: _ClassVar[int] + url: str + def __init__(self, url: _Optional[str] = ...) -> None: ... + +class ImagesDeleteUrlBaseRequest(_message.Message): + __slots__ = ("blob_key",) + BLOB_KEY_FIELD_NUMBER: _ClassVar[int] + blob_key: str + def __init__(self, blob_key: _Optional[str] = ...) -> None: ... + +class ImagesDeleteUrlBaseResponse(_message.Message): + __slots__ = () + def __init__(self) -> None: ... diff --git a/src/google/appengine/api/images/images_service_pb2_grpc.py b/src/google/appengine/api/images/images_service_pb2_grpc.py new file mode 100644 index 0000000..602e3df --- /dev/null +++ b/src/google/appengine/api/images/images_service_pb2_grpc.py @@ -0,0 +1,24 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc +import warnings + + +GRPC_GENERATED_VERSION = '1.71.0' +GRPC_VERSION = grpc.__version__ +_version_not_supported = False + +try: + from grpc._utilities import first_version_is_lower + _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) +except ImportError: + _version_not_supported = True + +if _version_not_supported: + raise RuntimeError( + f'The grpc package installed is at version {GRPC_VERSION},' + + f' but the generated code in images_service_pb2_grpc.py depends on' + + f' grpcio>={GRPC_GENERATED_VERSION}.' + + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' + ) diff --git a/src/google/appengine/api/images/images_service_rpc.proto b/src/google/appengine/api/images/images_service_rpc.proto new file mode 100644 index 0000000..8064699 --- /dev/null +++ b/src/google/appengine/api/images/images_service_rpc.proto @@ -0,0 +1,37 @@ +// Copyright 2008 Google Inc. All Rights Reserved. +// Author: ttrimble@google.com (Troy Trimble) + +// edition = "2023"; + +// Some generic_services option(s) added automatically. +// See: http://go/proto2-generic-services-default +package apphosting; + +import "images_service.proto"; + +service ImagesService { + // Perform transformations on the given image and return the result. + rpc Transform(ImagesTransformRequest) returns (ImagesTransformResponse) {} + + // Composite images onto a canvas and return the result. + rpc Composite(ImagesCompositeRequest) returns (ImagesCompositeResponse) {} + + // Calculate the histogram of an image and return it. + rpc Histogram(ImagesHistogramRequest) returns (ImagesHistogramResponse) {} + + // Obtains a URL that can serve the image dynamically at different sizes. + // The URL format will look like the following: + // + // http://lh3.ggpht.com/SomeEncryptedString + // + // to serve this image at different sizes just append the size option: + // + // http://lh3.ggpht.com/SomeEncryptedString=s128 (serves a 128 sized image) + // + // The allowed thumbnail sizes are any integer in the range [0, 1600]. + rpc GetUrlBase(ImagesGetUrlBaseRequest) returns (ImagesGetUrlBaseResponse) {} + + // Delete a URL that was created with GetUrlBase + rpc DeleteUrlBase(ImagesDeleteUrlBaseRequest) + returns (ImagesDeleteUrlBaseResponse) {} +} diff --git a/src/google/appengine/api/images/images_service_rpc_pb2.py b/src/google/appengine/api/images/images_service_rpc_pb2.py new file mode 100644 index 0000000..c8786fc --- /dev/null +++ b/src/google/appengine/api/images/images_service_rpc_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: images_service_rpc.proto +# Protobuf Python Version: 5.29.0 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 0, + '', + 'images_service_rpc.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +import images_service_pb2 as images__service__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18images_service_rpc.proto\x12\napphosting\x1a\x14images_service.proto2\xd6\x03\n\rImagesService\x12V\n\tTransform\x12\".apphosting.ImagesTransformRequest\x1a#.apphosting.ImagesTransformResponse\"\x00\x12V\n\tComposite\x12\".apphosting.ImagesCompositeRequest\x1a#.apphosting.ImagesCompositeResponse\"\x00\x12V\n\tHistogram\x12\".apphosting.ImagesHistogramRequest\x1a#.apphosting.ImagesHistogramResponse\"\x00\x12Y\n\nGetUrlBase\x12#.apphosting.ImagesGetUrlBaseRequest\x1a$.apphosting.ImagesGetUrlBaseResponse\"\x00\x12\x62\n\rDeleteUrlBase\x12&.apphosting.ImagesDeleteUrlBaseRequest\x1a\'.apphosting.ImagesDeleteUrlBaseResponse\"\x00') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'images_service_rpc_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + DESCRIPTOR._loaded_options = None + _globals['_IMAGESSERVICE']._serialized_start=63 + _globals['_IMAGESSERVICE']._serialized_end=533 +# @@protoc_insertion_point(module_scope) diff --git a/src/google/appengine/api/images/images_service_rpc_pb2.pyi b/src/google/appengine/api/images/images_service_rpc_pb2.pyi new file mode 100644 index 0000000..f9cc7c8 --- /dev/null +++ b/src/google/appengine/api/images/images_service_rpc_pb2.pyi @@ -0,0 +1,5 @@ +import images_service_pb2 as _images_service_pb2 +from google.protobuf import descriptor as _descriptor +from typing import ClassVar as _ClassVar + +DESCRIPTOR: _descriptor.FileDescriptor diff --git a/src/google/appengine/api/images/images_service_rpc_pb2_grpc.py b/src/google/appengine/api/images/images_service_rpc_pb2_grpc.py new file mode 100644 index 0000000..3c73535 --- /dev/null +++ b/src/google/appengine/api/images/images_service_rpc_pb2_grpc.py @@ -0,0 +1,283 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc +import warnings + +from . import images_service_pb2 as images__service__pb2 + +GRPC_GENERATED_VERSION = '1.71.0' +GRPC_VERSION = grpc.__version__ +_version_not_supported = False + +try: + from grpc._utilities import first_version_is_lower + _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) +except ImportError: + _version_not_supported = True + +if _version_not_supported: + raise RuntimeError( + f'The grpc package installed is at version {GRPC_VERSION},' + + f' but the generated code in images_service_rpc_pb2_grpc.py depends on' + + f' grpcio>={GRPC_GENERATED_VERSION}.' + + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' + ) + + +class ImagesServiceStub(object): + """Missing associated documentation comment in .proto file.""" + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.Transform = channel.unary_unary( + '/apphosting.ImagesService/Transform', + request_serializer=images__service__pb2.ImagesTransformRequest.SerializeToString, + response_deserializer=images__service__pb2.ImagesTransformResponse.FromString, + _registered_method=True) + self.Composite = channel.unary_unary( + '/apphosting.ImagesService/Composite', + request_serializer=images__service__pb2.ImagesCompositeRequest.SerializeToString, + response_deserializer=images__service__pb2.ImagesCompositeResponse.FromString, + _registered_method=True) + self.Histogram = channel.unary_unary( + '/apphosting.ImagesService/Histogram', + request_serializer=images__service__pb2.ImagesHistogramRequest.SerializeToString, + response_deserializer=images__service__pb2.ImagesHistogramResponse.FromString, + _registered_method=True) + self.GetUrlBase = channel.unary_unary( + '/apphosting.ImagesService/GetUrlBase', + request_serializer=images__service__pb2.ImagesGetUrlBaseRequest.SerializeToString, + response_deserializer=images__service__pb2.ImagesGetUrlBaseResponse.FromString, + _registered_method=True) + self.DeleteUrlBase = channel.unary_unary( + '/apphosting.ImagesService/DeleteUrlBase', + request_serializer=images__service__pb2.ImagesDeleteUrlBaseRequest.SerializeToString, + response_deserializer=images__service__pb2.ImagesDeleteUrlBaseResponse.FromString, + _registered_method=True) + + +class ImagesServiceServicer(object): + """Missing associated documentation comment in .proto file.""" + + def Transform(self, request, context): + """Perform transformations on the given image and return the result. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def Composite(self, request, context): + """Composite images onto a canvas and return the result. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def Histogram(self, request, context): + """Calculate the histogram of an image and return it. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def GetUrlBase(self, request, context): + """Obtains a URL that can serve the image dynamically at different sizes. + The URL format will look like the following: + + http://lh3.ggpht.com/SomeEncryptedString + + to serve this image at different sizes just append the size option: + + http://lh3.ggpht.com/SomeEncryptedString=s128 (serves a 128 sized image) + + The allowed thumbnail sizes are any integer in the range [0, 1600]. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def DeleteUrlBase(self, request, context): + """Delete a URL that was created with GetUrlBase + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_ImagesServiceServicer_to_server(servicer, server): + rpc_method_handlers = { + 'Transform': grpc.unary_unary_rpc_method_handler( + servicer.Transform, + request_deserializer=images__service__pb2.ImagesTransformRequest.FromString, + response_serializer=images__service__pb2.ImagesTransformResponse.SerializeToString, + ), + 'Composite': grpc.unary_unary_rpc_method_handler( + servicer.Composite, + request_deserializer=images__service__pb2.ImagesCompositeRequest.FromString, + response_serializer=images__service__pb2.ImagesCompositeResponse.SerializeToString, + ), + 'Histogram': grpc.unary_unary_rpc_method_handler( + servicer.Histogram, + request_deserializer=images__service__pb2.ImagesHistogramRequest.FromString, + response_serializer=images__service__pb2.ImagesHistogramResponse.SerializeToString, + ), + 'GetUrlBase': grpc.unary_unary_rpc_method_handler( + servicer.GetUrlBase, + request_deserializer=images__service__pb2.ImagesGetUrlBaseRequest.FromString, + response_serializer=images__service__pb2.ImagesGetUrlBaseResponse.SerializeToString, + ), + 'DeleteUrlBase': grpc.unary_unary_rpc_method_handler( + servicer.DeleteUrlBase, + request_deserializer=images__service__pb2.ImagesDeleteUrlBaseRequest.FromString, + response_serializer=images__service__pb2.ImagesDeleteUrlBaseResponse.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'apphosting.ImagesService', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) + server.add_registered_method_handlers('apphosting.ImagesService', rpc_method_handlers) + + + # This class is part of an EXPERIMENTAL API. +class ImagesService(object): + """Missing associated documentation comment in .proto file.""" + + @staticmethod + def Transform(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/apphosting.ImagesService/Transform', + images__service__pb2.ImagesTransformRequest.SerializeToString, + images__service__pb2.ImagesTransformResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def Composite(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/apphosting.ImagesService/Composite', + images__service__pb2.ImagesCompositeRequest.SerializeToString, + images__service__pb2.ImagesCompositeResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def Histogram(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/apphosting.ImagesService/Histogram', + images__service__pb2.ImagesHistogramRequest.SerializeToString, + images__service__pb2.ImagesHistogramResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def GetUrlBase(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/apphosting.ImagesService/GetUrlBase', + images__service__pb2.ImagesGetUrlBaseRequest.SerializeToString, + images__service__pb2.ImagesGetUrlBaseResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def DeleteUrlBase(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/apphosting.ImagesService/DeleteUrlBase', + images__service__pb2.ImagesDeleteUrlBaseRequest.SerializeToString, + images__service__pb2.ImagesDeleteUrlBaseResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) diff --git a/tests/google/appengine/api/images/images_test.py b/tests/google/appengine/api/images/images_test.py new file mode 100644 index 0000000..13e3b1f --- /dev/null +++ b/tests/google/appengine/api/images/images_test.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python +# +# Copyright 2007 Google LLC +# +# 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. +# +"""Tests for google.appengine.api.images.""" + +import os +from unittest import mock + +from absl.testing import absltest +from google.appengine.api.images import images_service_pb2 +from google.appengine.api import images + +class ImagesGrpcTest(absltest.TestCase): + + def setUp(self): + super().setUp() + os.environ['USE_CUSTOM_IMAGES_GRPC_SERVICE'] = 'True' + os.environ['IMAGES_GRPC_SERVICE_TARGET'] = 'localhost:8080' + os.environ['IMAGES_GRPC_SERVICE_TARGET_URL'] = 'http://localhost:8080' + + def tearDown(self): + super().tearDown() + del os.environ['USE_CUSTOM_IMAGES_GRPC_SERVICE'] + del os.environ['IMAGES_GRPC_SERVICE_TARGET'] + del os.environ['IMAGES_GRPC_SERVICE_TARGET_URL'] + + @mock.patch('google.appengine.api.images._make_grpc_call') + def test_execute_transforms_async_grpc(self, mock_make_grpc_call): + """Tests that execute_transforms_async calls the gRPC service.""" + mock_response = images_service_pb2.ImagesTransformResponse() + mock_response.image.content = b'transformed_image' + mock_make_grpc_call.return_value = mock_response + + image = images.Image(image_data=b'test_image') + image.resize(width=100, height=100) + rpc = image.execute_transforms_async() + result = rpc.get_result() + + self.assertEqual(result, b'transformed_image') + mock_make_grpc_call.assert_called_once() + self.assertEqual(mock_make_grpc_call.call_args[0][0], 'Transform') + + + @mock.patch('google.appengine.api.images._make_grpc_call') + def test_histogram_async_grpc(self, mock_make_grpc_call): + """Tests that histogram_async calls the gRPC service.""" + mock_response = images_service_pb2.ImagesHistogramResponse() + mock_response.histogram.red.extend(range(256)) + mock_response.histogram.green.extend(range(256)) + mock_response.histogram.blue.extend(range(256)) + mock_make_grpc_call.return_value = mock_response + + image = images.Image(image_data=b'test_image') + rpc = image.histogram_async() + result = rpc.get_result() + + self.assertEqual(result[0], list(range(256))) + self.assertEqual(result[1], list(range(256))) + self.assertEqual(result[2], list(range(256))) + mock_make_grpc_call.assert_called_once() + self.assertEqual(mock_make_grpc_call.call_args[0][0], 'Histogram') + + + @mock.patch('google.appengine.api.images._make_grpc_call') + def test_composite_async_grpc(self, mock_make_grpc_call): + """Tests that composite_async calls the gRPC service.""" + mock_response = images_service_pb2.ImagesTransformResponse() + mock_response.image.content = b'composited_image' + mock_make_grpc_call.return_value = mock_response + + inputs = [ + (b'test_image_1', 0, 0, 1.0, images.TOP_LEFT), + (b'test_image_2', 10, 10, 0.5, images.TOP_LEFT), + ] + rpc = images.composite_async( + inputs=inputs, + width=200, + height=200, + color=0xFFFFFFFF, + output_encoding=images.PNG, + ) + result = rpc.get_result() + + self.assertEqual(result, b'composited_image') + mock_make_grpc_call.assert_called_once() + self.assertEqual(mock_make_grpc_call.call_args[0][0], 'Composite') + + +if __name__ == '__main__': + absltest.main() diff --git a/tests/google/appengine/api/taskqueue/taskqueue_test.py b/tests/google/appengine/api/taskqueue/taskqueue_test.py index afac5d2..a5cae5f 100755 --- a/tests/google/appengine/api/taskqueue/taskqueue_test.py +++ b/tests/google/appengine/api/taskqueue/taskqueue_test.py @@ -34,6 +34,7 @@ from google.appengine.api import apiproxy_stub_map from google.appengine.api import datastore from google.appengine.api import datastore_errors +from google.appengine.api import datastore_file_stub from google.appengine.api import full_app_id from google.appengine.api import module_testutil from google.appengine.api import modules @@ -3364,8 +3365,13 @@ def setUp(self): full_app_id.put(self.APP_ID) + def SetupMox(self): self.mox = mox.Mox() + apiproxy_stub_map.apiproxy = apiproxy_stub_map.APIProxyStubMap() + self.datastore_stub = datastore_file_stub.DatastoreFileStub( + 'test-app-id', '/dev/null', '/dev/null') + apiproxy_stub_map.apiproxy.RegisterStub('datastore_v3', self.datastore_stub) self.mox.StubOutWithMock(apiproxy_stub_map, 'CreateRPC') def tearDown(self): @@ -3499,8 +3505,15 @@ def transaction(): q, [Task(method='PULL', payload=longstring) for _ in range(3)], transactional=True) + def SetDatastoreResponse(service, method, request, response): + response.app = 'app' + response.handle = self.tx_handle + apiproxy_stub_map.CreateRPC('datastore_v3', - mox.IgnoreArg()).WithSideEffects(MockRPC) + mox.IgnoreArg()).WithSideEffects( + functools.partial( + MockRPC, + populate_response=SetDatastoreResponse)) apiproxy_stub_map.CreateRPC('datastore_v3', mox.IgnoreArg()).WithSideEffects(MockRPC) diff --git a/tests/google/appengine/ext/ndb/msgprop_test.py b/tests/google/appengine/ext/ndb/msgprop_test.py index d258aca..97dc024 100755 --- a/tests/google/appengine/ext/ndb/msgprop_test.py +++ b/tests/google/appengine/ext/ndb/msgprop_test.py @@ -45,43 +45,10 @@ class Color(messages.Enum): BLUE = 450 -SAMPLE_PB1 = r"""key < - app: "ndb-test-app-id" - path < - Element { - type: "Storage" - id: 1 - } - > -> -entity_group < - Element { - type: "Storage" - id: 1 - } -> -property < - name: "greet.text" - value < - stringValue: "abc" - > - multiple: false -> -raw_property < - meaning: 14 - name: "greet.__protobuf__" - value < - stringValue: "\n\003abc\020{" - > - multiple: false -> -""" - - SAMPLE_PB2 = r"""key { app: "ndb-test-app-id" path { - Element { + element { type: "Storage" id: 1 } @@ -103,7 +70,7 @@ class Color(messages.Enum): } } entity_group { - Element { + element { type: "Storage" id: 1 } @@ -139,12 +106,7 @@ class Storage(model.Model): self.assertEqual(result.greet, Greeting(when=123, text='abc')) self.assertEqual(result, Storage(greet=Greeting(when=123, text='abc'), key=key)) - try: - self.assertEqual(str(result._to_pb()), SAMPLE_PB2) - except AssertionError: - - - self.assertEqual(str(result._to_pb()), SAMPLE_PB1) + self.assertEqual(str(result._to_pb()), SAMPLE_PB2) def testValidator(self): logs = [] diff --git a/tests/google/appengine/ext/ndb/polymodel_test.py b/tests/google/appengine/ext/ndb/polymodel_test.py index 5608e57..44ed48a 100755 --- a/tests/google/appengine/ext/ndb/polymodel_test.py +++ b/tests/google/appengine/ext/ndb/polymodel_test.py @@ -344,7 +344,7 @@ class Cat(Animal): key { app: "ndb-test-app-id" path { - Element { + element { type: "Animal" } } @@ -399,7 +399,7 @@ class Cat(Animal): key < app: "ndb-test-app-id" path < - Element { + element { type: "Animal" } > diff --git a/tox.ini b/tox.ini index 3e65358..23bc5ff 100644 --- a/tox.ini +++ b/tox.ini @@ -7,7 +7,7 @@ envlist = py{37,38,39,310,311} [testenv] setenv = GAE_RUNTIME = python3 -usedevelop = true + deps = absl-py attrs @@ -30,4 +30,4 @@ deps = ruamel.yaml < 0.18 six urllib3 -commands = pytest --cov=google.appengine {posargs} +commands = pytest --cov=google.appengine --ignore=src {posargs}