1# Copyright 2021 Google LLC 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14"""FileObjectAdapter class. 15 16Used in conjunction with PythonOutputStream/PythonInputStream to allow a C++ 17OutputStream/InputStream to interact with a Python file-like object. 18""" 19 20import io 21from typing import BinaryIO 22 23from tink.cc.pybind import tink_bindings 24 25 26class FileObjectAdapter(tink_bindings.PythonFileObjectAdapter): 27 """Adapts a Python file object for use in C++.""" 28 29 def __init__(self, file_object: BinaryIO): 30 # Required to fix CLIF "Value invalidated due to capture by std::unique_ptr" 31 super().__init__() 32 self._file_object = file_object 33 34 def write(self, data: bytes) -> int: 35 """Writes to underlying file object and returns number of bytes written.""" 36 try: 37 written = self._file_object.write(data) 38 return 0 if written is None else written 39 except io.BlockingIOError as e: 40 return e.characters_written 41 42 def close(self) -> None: 43 self._file_object.close() 44 45 def read(self, size: int) -> bytes: 46 """Reads at most 'size' bytes from the underlying file object. 47 48 Args: 49 size: A non-negative integer, maximum number of bytes to read. 50 51 Returns: 52 Bytes that were read. An empty bytes object is returned if no bytes are 53 available at the moment. 54 55 Raises: 56 EOFError if the file object is already at EOF. 57 """ 58 if size < 0: 59 raise ValueError('size must be non-negative') 60 61 try: 62 data = self._file_object.read(size) 63 if data is None: 64 return b'' 65 elif not data and size > 0: 66 raise EOFError('EOF') 67 return data 68 except io.BlockingIOError: 69 return b'' 70