From 59ddbc4e930035286c7684402b5c57266ebf70d3 Mon Sep 17 00:00:00 2001 From: Michael Banfield Date: Thu, 18 Jun 2020 17:33:58 +0000 Subject: [PATCH 01/85] Add a standalone binary build for GCS ops --- tensorflow_io/core/plugins/http/BUILD | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tensorflow_io/core/plugins/http/BUILD b/tensorflow_io/core/plugins/http/BUILD index b9bbc9066..7570b1c3a 100644 --- a/tensorflow_io/core/plugins/http/BUILD +++ b/tensorflow_io/core/plugins/http/BUILD @@ -7,6 +7,15 @@ load( "tf_io_copts", ) +cc_binary( + name = "_gcs_config_ops.so", + copts = tf_io_copts(), + linkshared = 1, + deps = [ + ":gcs_config_ops", + ], +) + cc_library( name = "http", srcs = [ From ca8e327163751a67147dda9c29cd807a076a7e5a Mon Sep 17 00:00:00 2001 From: Michael Banfield Date: Mon, 14 Dec 2020 21:46:30 +0000 Subject: [PATCH 02/85] Revert "Deprecate gcs-config (#1024)" This reverts commit 9702a1505a5ce3204b88169469d391f62c66328d. --- tensorflow_io/core/BUILD | 12 - tensorflow_io/gcs/BUILD | 25 ++ tensorflow_io/gcs/README.md | 3 + tensorflow_io/gcs/__init__.py | 29 +++ .../gcs/kernels/gcs_config_op_kernels.cc | 206 +++++++++++++++ tensorflow_io/gcs/ops/gcs_config_ops.cc | 66 +++++ tensorflow_io/gcs/python/__init__.py | 16 ++ tensorflow_io/gcs/python/ops/__init__.py | 16 ++ .../gcs/python/ops/gcs_config_ops.py | 235 ++++++++++++++++++ tests/test_gcs_config_ops.py | 46 ++++ 10 files changed, 642 insertions(+), 12 deletions(-) create mode 100644 tensorflow_io/gcs/BUILD create mode 100644 tensorflow_io/gcs/README.md create mode 100644 tensorflow_io/gcs/__init__.py create mode 100644 tensorflow_io/gcs/kernels/gcs_config_op_kernels.cc create mode 100644 tensorflow_io/gcs/ops/gcs_config_ops.cc create mode 100644 tensorflow_io/gcs/python/__init__.py create mode 100644 tensorflow_io/gcs/python/ops/__init__.py create mode 100644 tensorflow_io/gcs/python/ops/gcs_config_ops.py create mode 100644 tests/test_gcs_config_ops.py diff --git a/tensorflow_io/core/BUILD b/tensorflow_io/core/BUILD index 3563b0432..a7d381034 100644 --- a/tensorflow_io/core/BUILD +++ b/tensorflow_io/core/BUILD @@ -744,18 +744,6 @@ cc_binary( }), ) -cc_binary( - name = "python/ops/libtensorflow_io_plugins.so", - copts = tf_io_copts(), - linkshared = 1, - deps = select({ - "//tensorflow_io/core:static_build_on": [], - "//conditions:default": [ - "//tensorflow_io/core/plugins:plugins", - ], - }), -) - cc_binary( name = "python/ops/libtensorflow_io_golang.so", copts = tf_io_copts(), diff --git a/tensorflow_io/gcs/BUILD b/tensorflow_io/gcs/BUILD new file mode 100644 index 000000000..1c66d0860 --- /dev/null +++ b/tensorflow_io/gcs/BUILD @@ -0,0 +1,25 @@ +licenses(["notice"]) # Apache 2.0 + +package(default_visibility = ["//visibility:public"]) + +load( + "//:tools/build/tensorflow_io.bzl", + "tf_io_copts", +) + +cc_library( + name = "gcs_config_ops", + srcs = [ + "kernels/gcs_config_op_kernels.cc", + "ops/gcs_config_ops.cc", + ], + copts = tf_io_copts(), + linkstatic = True, + deps = [ + "@curl", + "@jsoncpp_git//:jsoncpp", + "@local_config_tf//:libtensorflow_framework", + "@local_config_tf//:tf_header_lib", + ], + alwayslink = 1, +) diff --git a/tensorflow_io/gcs/README.md b/tensorflow_io/gcs/README.md new file mode 100644 index 000000000..99782a341 --- /dev/null +++ b/tensorflow_io/gcs/README.md @@ -0,0 +1,3 @@ +## Cloud Storage (GCS) ## + +The Google Cloud Storage ops allow the user to configure the GCS File System. diff --git a/tensorflow_io/gcs/__init__.py b/tensorflow_io/gcs/__init__.py new file mode 100644 index 000000000..39f6154b7 --- /dev/null +++ b/tensorflow_io/gcs/__init__.py @@ -0,0 +1,29 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +"""Module for cloud ops.""" + + +from tensorflow.python.util.all_util import remove_undocumented + +# pylint: disable=line-too-long,wildcard-import,g-import-not-at-top +from tensorflow_io.gcs.python.ops.gcs_config_ops import * + +_allowed_symbols = [ + "configure_colab_session", + "configure_gcs", + "BlockCacheParams", + "ConfigureGcsHook", +] +remove_undocumented(__name__, _allowed_symbols) diff --git a/tensorflow_io/gcs/kernels/gcs_config_op_kernels.cc b/tensorflow_io/gcs/kernels/gcs_config_op_kernels.cc new file mode 100644 index 000000000..3fd878a73 --- /dev/null +++ b/tensorflow_io/gcs/kernels/gcs_config_op_kernels.cc @@ -0,0 +1,206 @@ +/* Copyright 2019 The TensorFlow Authors. All Rights Reserved. + +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. +==============================================================================*/ + +#include + +#include "include/json/json.h" +#include "tensorflow/core/framework/op_kernel.h" +#include "tensorflow/core/framework/tensor_shape.h" +#include "tensorflow/core/platform/cloud/curl_http_request.h" +#include "tensorflow/core/platform/cloud/gcs_file_system.h" +#include "tensorflow/core/platform/cloud/oauth_client.h" +#include "tensorflow/core/util/ptr_util.h" + +namespace tensorflow { +namespace { + +// The default initial delay between retries with exponential backoff. +constexpr int kInitialRetryDelayUsec = 500000; // 0.5 sec + +// The minimum time delta between now and the token expiration time +// for the token to be re-used. +constexpr int kExpirationTimeMarginSec = 60; + +// The URL to retrieve the auth bearer token via OAuth with a refresh token. +constexpr char kOAuthV3Url[] = "https://www.googleapis.com/oauth2/v3/token"; + +// The URL to retrieve the auth bearer token via OAuth with a private key. +constexpr char kOAuthV4Url[] = "https://www.googleapis.com/oauth2/v4/token"; + +// The authentication token scope to request. +constexpr char kOAuthScope[] = "https://www.googleapis.com/auth/cloud-platform"; + +Status RetrieveGcsFs(OpKernelContext* ctx, RetryingGcsFileSystem** fs) { + DCHECK(fs != nullptr); + *fs = nullptr; + + FileSystem* filesystem = nullptr; + TF_RETURN_IF_ERROR( + ctx->env()->GetFileSystemForFile("gs://fake/file.text", &filesystem)); + if (filesystem == nullptr) { + return errors::FailedPrecondition("The GCS file system is not registered."); + } + + *fs = dynamic_cast(filesystem); + if (*fs == nullptr) { + return errors::Internal( + "The filesystem registered under the 'gs://' scheme was not a " + "tensorflow::RetryingGcsFileSystem*."); + } + return Status::OK(); +} + +template +Status ParseScalarArgument(OpKernelContext* ctx, StringPiece argument_name, + T* output) { + const Tensor* argument_t; + TF_RETURN_IF_ERROR(ctx->input(argument_name, &argument_t)); + if (!TensorShapeUtils::IsScalar(argument_t->shape())) { + return errors::InvalidArgument(argument_name, " must be a scalar"); + } + *output = argument_t->scalar()(); + return Status::OK(); +} + +// GcsCredentialsOpKernel overrides the credentials used by the gcs_filesystem. +class GcsCredentialsOpKernel : public OpKernel { + public: + explicit GcsCredentialsOpKernel(OpKernelConstruction* ctx) : OpKernel(ctx) {} + void Compute(OpKernelContext* ctx) override { + // Get a handle to the GCS file system. + RetryingGcsFileSystem* gcs = nullptr; + OP_REQUIRES_OK(ctx, RetrieveGcsFs(ctx, &gcs)); + + tstring json_string; + OP_REQUIRES_OK(ctx, + ParseScalarArgument(ctx, "json", &json_string)); + + Json::Value json; + Json::Reader reader; + std::stringstream json_stream(json_string); + OP_REQUIRES(ctx, reader.parse(json_stream, json), + errors::InvalidArgument("Could not parse json: ", json_string)); + + OP_REQUIRES( + ctx, json.isMember("refresh_token") || json.isMember("private_key"), + errors::InvalidArgument("JSON format incompatible; did not find fields " + "`refresh_token` or `private_key`.")); + + auto provider = + tensorflow::MakeUnique(json, ctx->env()); + + // Test getting a token + string dummy_token; + OP_REQUIRES_OK(ctx, provider->GetToken(&dummy_token)); + OP_REQUIRES(ctx, !dummy_token.empty(), + errors::InvalidArgument( + "Could not retrieve a token with the given credentials.")); + + // Set the provider. + gcs->underlying()->SetAuthProvider(std::move(provider)); + } + + private: + class ConstantAuthProvider : public AuthProvider { + public: + ConstantAuthProvider(const Json::Value& json, + std::unique_ptr oauth_client, Env* env, + int64 initial_retry_delay_usec) + : json_(json), + oauth_client_(std::move(oauth_client)), + env_(env), + initial_retry_delay_usec_(initial_retry_delay_usec) {} + + ConstantAuthProvider(const Json::Value& json, Env* env) + : ConstantAuthProvider(json, tensorflow::MakeUnique(), env, + kInitialRetryDelayUsec) {} + + ~ConstantAuthProvider() override {} + + Status GetToken(string* token) override { + mutex_lock l(mu_); + const uint64 now_sec = env_->NowSeconds(); + + if (!current_token_.empty() && + now_sec + kExpirationTimeMarginSec < expiration_timestamp_sec_) { + *token = current_token_; + return Status::OK(); + } + if (json_.isMember("refresh_token")) { + TF_RETURN_IF_ERROR(oauth_client_->GetTokenFromRefreshTokenJson( + json_, kOAuthV3Url, ¤t_token_, &expiration_timestamp_sec_)); + } else if (json_.isMember("private_key")) { + TF_RETURN_IF_ERROR(oauth_client_->GetTokenFromServiceAccountJson( + json_, kOAuthV4Url, kOAuthScope, ¤t_token_, + &expiration_timestamp_sec_)); + } else { + return errors::FailedPrecondition( + "Unexpected content of the JSON credentials file."); + } + + *token = current_token_; + return Status::OK(); + } + + private: + Json::Value json_; + std::unique_ptr oauth_client_; + Env* env_; + + mutex mu_; + string current_token_ TF_GUARDED_BY(mu_); + uint64 expiration_timestamp_sec_ TF_GUARDED_BY(mu_) = 0; + + // The initial delay for exponential backoffs when retrying failed calls. + const int64 initial_retry_delay_usec_; + TF_DISALLOW_COPY_AND_ASSIGN(ConstantAuthProvider); + }; +}; + +REGISTER_KERNEL_BUILDER(Name("IO>GcsConfigureCredentials").Device(DEVICE_CPU), + GcsCredentialsOpKernel); + +class GcsBlockCacheOpKernel : public OpKernel { + public: + explicit GcsBlockCacheOpKernel(OpKernelConstruction* ctx) : OpKernel(ctx) {} + void Compute(OpKernelContext* ctx) override { + // Get a handle to the GCS file system. + RetryingGcsFileSystem* gcs = nullptr; + OP_REQUIRES_OK(ctx, RetrieveGcsFs(ctx, &gcs)); + + size_t max_cache_size, block_size, max_staleness; + OP_REQUIRES_OK(ctx, ParseScalarArgument(ctx, "max_cache_size", + &max_cache_size)); + OP_REQUIRES_OK(ctx, + ParseScalarArgument(ctx, "block_size", &block_size)); + OP_REQUIRES_OK( + ctx, ParseScalarArgument(ctx, "max_staleness", &max_staleness)); + + if (gcs->underlying()->block_size() == block_size && + gcs->underlying()->max_bytes() == max_cache_size && + gcs->underlying()->max_staleness() == max_staleness) { + LOG(INFO) << "Skipping resetting the GCS block cache."; + return; + } + gcs->underlying()->ResetFileBlockCache(block_size, max_cache_size, + max_staleness); + } +}; + +REGISTER_KERNEL_BUILDER(Name("IO>GcsConfigureBlockCache").Device(DEVICE_CPU), + GcsBlockCacheOpKernel); + +} // namespace +} // namespace tensorflow diff --git a/tensorflow_io/gcs/ops/gcs_config_ops.cc b/tensorflow_io/gcs/ops/gcs_config_ops.cc new file mode 100644 index 000000000..140dbc3a3 --- /dev/null +++ b/tensorflow_io/gcs/ops/gcs_config_ops.cc @@ -0,0 +1,66 @@ +/* Copyright 2019 The TensorFlow Authors. All Rights Reserved. + +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. +==============================================================================*/ + +#include "tensorflow/core/framework/common_shape_fns.h" +#include "tensorflow/core/framework/op.h" +#include "tensorflow/core/framework/shape_inference.h" + +namespace tensorflow { + +using shape_inference::InferenceContext; + +REGISTER_OP("IO>GcsConfigureCredentials") + .Input("json: string") + .SetShapeFn(shape_inference::NoOutputs) + .Doc(R"doc( +Configures the credentials used by the GCS client of the local TF runtime. +The json input can be of the format: +1. Refresh Token: +{ + "client_id": "", + "client_secret": "", + "refresh_token: "", + "type": "authorized_user", +} +2. Service Account: +{ + "type": "service_account", + "project_id": "", + "private_key_id": "", + "private_key": "------BEGIN PRIVATE KEY-----\n\n-----END PRIVATE KEY------\n", + "client_email": "@.iam.gserviceaccount.com", + "client_id": "", + # Some additional fields elided +} +Note the credentials established through this method are shared across all +sessions run on this runtime. +Note be sure to feed the inputs to this op to ensure the credentials are not +stored in a constant op within the graph that might accidentally be checkpointed +or in other ways be persisted or exfiltrated. +)doc"); + +REGISTER_OP("IO>GcsConfigureBlockCache") + .Input("max_cache_size: uint64") + .Input("block_size: uint64") + .Input("max_staleness: uint64") + .SetShapeFn(shape_inference::NoOutputs) + .Doc(R"doc( +Re-configures the GCS block cache with the new configuration values. +If the values are the same as already configured values, this op is a no-op. If +they are different, the current contents of the block cache is dropped, and a +new block cache is created fresh. +)doc"); + +} // namespace tensorflow diff --git a/tensorflow_io/gcs/python/__init__.py b/tensorflow_io/gcs/python/__init__.py new file mode 100644 index 000000000..f00d24fd2 --- /dev/null +++ b/tensorflow_io/gcs/python/__init__.py @@ -0,0 +1,16 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""This module contains Python API methods for GCS integration.""" diff --git a/tensorflow_io/gcs/python/ops/__init__.py b/tensorflow_io/gcs/python/ops/__init__.py new file mode 100644 index 000000000..568c0e67a --- /dev/null +++ b/tensorflow_io/gcs/python/ops/__init__.py @@ -0,0 +1,16 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""This module contains the Python API methods for GCS integration.""" diff --git a/tensorflow_io/gcs/python/ops/gcs_config_ops.py b/tensorflow_io/gcs/python/ops/gcs_config_ops.py new file mode 100644 index 000000000..148602fe1 --- /dev/null +++ b/tensorflow_io/gcs/python/ops/gcs_config_ops.py @@ -0,0 +1,235 @@ +# Copyright 2016 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +"""GCS file system configuration for TensorFlow.""" + + +import json +import os + +import tensorflow as tf +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import ops +from tensorflow.python.ops import array_ops +from tensorflow.python.training import training +from tensorflow_io.core.python.ops import core_ops + +# Some GCS operations may be pre-defined and available via tf.contrib in +# earlier TF versions. Because these ops are pre-registered, they will not be +# visible from the _gcs_config_ops library. In this case we use the tf.contrib +# version instead. +tf_v1 = tf.version.VERSION.startswith("1") + +if not tf_v1: + gcs_configure_credentials = core_ops.io_gcs_configure_credentials + gcs_configure_block_cache = core_ops.io_gcs_configure_block_cache + + +class BlockCacheParams: # pylint: disable=useless-object-inheritance + """BlockCacheParams is a struct used for configuring the GCS Block Cache.""" + + def __init__(self, block_size=None, max_bytes=None, max_staleness=None): + self._block_size = block_size or 128 * 1024 * 1024 + self._max_bytes = max_bytes or 2 * self._block_size + self._max_staleness = max_staleness or 0 + + @property + def block_size(self): + return self._block_size + + @property + def max_bytes(self): + return self._max_bytes + + @property + def max_staleness(self): + return self._max_staleness + + +class ConfigureGcsHook(training.SessionRunHook): + """ConfigureGcsHook configures GCS when used with Estimator/TPUEstimator. + + Warning: GCS `credentials` may be transmitted over the network unencrypted. + Please ensure that the network is trusted before using this function. For + users running code entirely within Google Cloud, your data is protected by + encryption in between data centers. For more information, please take a look + at https://cloud.google.com/security/encryption-in-transit/. + + Example: + + ``` + sess = tf.Session() + refresh_token = raw_input("Refresh token: ") + client_secret = raw_input("Client secret: ") + client_id = "" + creds = { + "client_id": client_id, + "refresh_token": refresh_token, + "client_secret": client_secret, + "type": "authorized_user", + } + tf.contrib.cloud.configure_gcs(sess, credentials=creds) + ``` + + """ + + def _verify_dictionary(self, creds_dict): + if "refresh_token" in creds_dict or "private_key" in creds_dict: + return True + return False + + def __init__(self, credentials=None, block_cache=None): + """Constructs a ConfigureGcsHook. + + Args: + credentials: A json-formatted string. + block_cache: A `BlockCacheParams` + + Raises: + ValueError: If credentials is improperly formatted or block_cache is not a + BlockCacheParams. + """ + if credentials is not None: + if isinstance(credentials, str): + try: + data = json.loads(credentials) + except ValueError as e: + raise ValueError( + "credentials was not a well formed JSON string.", e + ) + if not self._verify_dictionary(data): + raise ValueError( + 'credentials has neither a "refresh_token" nor a "private_key" ' + "field." + ) + elif isinstance(credentials, dict): + if not self._verify_dictionary(credentials): + raise ValueError( + 'credentials has neither a "refresh_token" nor a ' + '"private_key" field.' + ) + credentials = json.dumps(credentials) + else: + raise ValueError("credentials is of an unknown type") + + self._credentials = credentials + + if block_cache and not isinstance(block_cache, BlockCacheParams): + raise ValueError("block_cache must be an instance of BlockCacheParams.") + self._block_cache = block_cache + + def begin(self): + """Called once before using the session. + + When called, the default graph is the one that will be launched in the + session. The hook can modify the graph by adding new operations to it. + After the `begin()` call the graph will be finalized and the other callbacks + can not modify the graph anymore. Second call of `begin()` on the same + graph, should not change the graph. + """ + if self._credentials: + self._credentials_placeholder = array_ops.placeholder(dtypes.string) + self._credentials_op = gcs_configure_credentials( + self._credentials_placeholder + ) + else: + self._credentials_op = None + + if self._block_cache: + self._block_cache_op = gcs_configure_block_cache( + max_cache_size=self._block_cache.max_bytes, + block_size=self._block_cache.block_size, + max_staleness=self._block_cache.max_staleness, + ) + else: + self._block_cache_op = None + + def after_create_session(self, session, coord): + """Called when new TensorFlow session is created. + + This is called to signal the hooks that a new session has been created. This + has two essential differences with the situation in which `begin` is called: + + * When this is called, the graph is finalized and ops can no longer be added + to the graph. + * This method will also be called as a result of recovering a wrapped + session, not only at the beginning of the overall session. + + Args: + session: A TensorFlow Session that has been created. + coord: A Coordinator object which keeps track of all threads. + """ + del coord + if self._credentials_op: + session.run( + self._credentials_op, + feed_dict={self._credentials_placeholder: self._credentials}, + ) + if self._block_cache_op: + session.run(self._block_cache_op) + + +def _configure_gcs_tfv2(credentials=None, block_cache=None, device=None): + """Configures the GCS file system for a given a session. + + Warning: GCS `credentials` may be transmitted over the network unencrypted. + Please ensure that the network is trusted before using this function. For + users running code entirely within Google Cloud, your data is protected by + encryption in between data centers. For more information, please take a look + at https://cloud.google.com/security/encryption-in-transit/. + + Args: + credentials: [Optional.] A JSON string + block_cache: [Optional.] A BlockCacheParams to configure the block cache . + device: [Optional.] The device to place the configure ops. + """ + + def configure(credentials, block_cache): + """Helper function to actually configure GCS.""" + if credentials: + if isinstance(credentials, dict): + credentials = json.dumps(credentials) + gcs_configure_credentials(credentials) + + if block_cache: + gcs_configure_block_cache( + max_cache_size=block_cache.max_bytes, + block_size=block_cache.block_size, + max_staleness=block_cache.max_staleness, + ) + + if device: + with ops.device(device): + return configure(credentials, block_cache) + return configure(credentials, block_cache) + + +def _configure_colab_session_tfv2(): + """ConfigureColabSession configures the GCS file system in Colab. + + Args: + """ + # Read from the application default credentials (adc). + adc_filename = os.environ.get("GOOGLE_APPLICATION_CREDENTIALS", "/content/adc.json") + with open(adc_filename) as f: + data = json.load(f) + configure_gcs(credentials=data) + + +if tf_v1: + configure_gcs = tf.contrib.cloud.configure_gcs + configure_colab_session = tf.contrib.cloud.configure_colab_session +else: + configure_gcs = _configure_gcs_tfv2 + configure_colab_session = _configure_colab_session_tfv2 diff --git a/tests/test_gcs_config_ops.py b/tests/test_gcs_config_ops.py new file mode 100644 index 000000000..7d0140900 --- /dev/null +++ b/tests/test_gcs_config_ops.py @@ -0,0 +1,46 @@ +# Copyright 2016 The TensorFlow Authors. All Rights Reserved. +# +# 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 the gcs_config_ops.""" + + +import sys +import pytest + +import tensorflow as tf + +from tensorflow.python.platform import test +from tensorflow_io import gcs + +tf_v1 = tf.version.VERSION.startswith("1") + + +class GcsConfigOpsTest(test.TestCase): + """GCS Config OPS test""" + + @pytest.mark.skipif(sys.platform == "darwin", reason=None) + def test_set_block_cache(self): + """test_set_block_cache""" + cfg = gcs.BlockCacheParams(max_bytes=1024 * 1024 * 1024) + if tf_v1: + with tf.Session() as session: + gcs.configure_gcs( + session, credentials=None, block_cache=cfg, device=None + ) + else: + gcs.configure_gcs(block_cache=cfg) + + +if __name__ == "__main__": + test.main() From cb8ffe73a9f3a1fbe06d2a987ff27e190fc4c81b Mon Sep 17 00:00:00 2001 From: Michael Banfield Date: Mon, 14 Dec 2020 22:18:29 +0000 Subject: [PATCH 03/85] Rebase change --- tensorflow_io/gcs/BUILD | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tensorflow_io/gcs/BUILD b/tensorflow_io/gcs/BUILD index 1c66d0860..a1b8523f4 100644 --- a/tensorflow_io/gcs/BUILD +++ b/tensorflow_io/gcs/BUILD @@ -7,6 +7,16 @@ load( "tf_io_copts", ) +cc_binary( + name = "_gcs_config_ops.so", + copts = tf_io_copts(), + linkshared = 1, + deps = [ + ":gcs_config_ops", + ], +) + + cc_library( name = "gcs_config_ops", srcs = [ From 35363382dad16c48fdd6afce597715a63f78899c Mon Sep 17 00:00:00 2001 From: Michael Banfield Date: Mon, 14 Dec 2020 22:53:38 +0000 Subject: [PATCH 04/85] Clean up merge --- tensorflow_io/core/BUILD | 12 ++++++++++++ tensorflow_io/core/plugins/http/BUILD | 9 --------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/tensorflow_io/core/BUILD b/tensorflow_io/core/BUILD index a7d381034..3563b0432 100644 --- a/tensorflow_io/core/BUILD +++ b/tensorflow_io/core/BUILD @@ -744,6 +744,18 @@ cc_binary( }), ) +cc_binary( + name = "python/ops/libtensorflow_io_plugins.so", + copts = tf_io_copts(), + linkshared = 1, + deps = select({ + "//tensorflow_io/core:static_build_on": [], + "//conditions:default": [ + "//tensorflow_io/core/plugins:plugins", + ], + }), +) + cc_binary( name = "python/ops/libtensorflow_io_golang.so", copts = tf_io_copts(), diff --git a/tensorflow_io/core/plugins/http/BUILD b/tensorflow_io/core/plugins/http/BUILD index 7570b1c3a..b9bbc9066 100644 --- a/tensorflow_io/core/plugins/http/BUILD +++ b/tensorflow_io/core/plugins/http/BUILD @@ -7,15 +7,6 @@ load( "tf_io_copts", ) -cc_binary( - name = "_gcs_config_ops.so", - copts = tf_io_copts(), - linkshared = 1, - deps = [ - ":gcs_config_ops", - ], -) - cc_library( name = "http", srcs = [ From 11c8a5153e2505ab5abfbf07dfd37ac90c6f6b6f Mon Sep 17 00:00:00 2001 From: Michael Banfield Date: Mon, 14 Dec 2020 23:16:07 +0000 Subject: [PATCH 05/85] Fix lint errors --- tensorflow_io/gcs/BUILD | 1 - 1 file changed, 1 deletion(-) diff --git a/tensorflow_io/gcs/BUILD b/tensorflow_io/gcs/BUILD index a1b8523f4..8ffb13dbd 100644 --- a/tensorflow_io/gcs/BUILD +++ b/tensorflow_io/gcs/BUILD @@ -16,7 +16,6 @@ cc_binary( ], ) - cc_library( name = "gcs_config_ops", srcs = [ From 3560ac6bd743afdd711025d3bea0d5000bb9fe95 Mon Sep 17 00:00:00 2001 From: Michael Banfield Date: Mon, 29 Mar 2021 09:27:57 -0700 Subject: [PATCH 06/85] Skip tests for windows --- tensorflow_io/core/BUILD | 1 + tests/test_gcs_config_ops.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tensorflow_io/core/BUILD b/tensorflow_io/core/BUILD index 3563b0432..f1a20b198 100644 --- a/tensorflow_io/core/BUILD +++ b/tensorflow_io/core/BUILD @@ -724,6 +724,7 @@ cc_binary( "//tensorflow_io/core:text_ops", "//tensorflow_io/core:ignite_ops", "//tensorflow_io/core:mongodb_ops", + "//tensorflow_io/gcs:gcs_config_ops", "@local_config_tf//:libtensorflow_framework", "@local_config_tf//:tf_header_lib", ] + select({ diff --git a/tests/test_gcs_config_ops.py b/tests/test_gcs_config_ops.py index 7d0140900..291986c22 100644 --- a/tests/test_gcs_config_ops.py +++ b/tests/test_gcs_config_ops.py @@ -29,7 +29,7 @@ class GcsConfigOpsTest(test.TestCase): """GCS Config OPS test""" - @pytest.mark.skipif(sys.platform == "darwin", reason=None) + @pytest.mark.skipif(sys.platform == "win32", reason="Windows not working yet") def test_set_block_cache(self): """test_set_block_cache""" cfg = gcs.BlockCacheParams(max_bytes=1024 * 1024 * 1024) From 42c2867acd6bfcd76f64bed93d53283449680ebd Mon Sep 17 00:00:00 2001 From: Michael Banfield Date: Mon, 29 Mar 2021 11:47:17 -0700 Subject: [PATCH 07/85] Move build target to exclude windows --- tensorflow_io/core/BUILD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow_io/core/BUILD b/tensorflow_io/core/BUILD index f1a20b198..87f5b68cc 100644 --- a/tensorflow_io/core/BUILD +++ b/tensorflow_io/core/BUILD @@ -724,7 +724,6 @@ cc_binary( "//tensorflow_io/core:text_ops", "//tensorflow_io/core:ignite_ops", "//tensorflow_io/core:mongodb_ops", - "//tensorflow_io/gcs:gcs_config_ops", "@local_config_tf//:libtensorflow_framework", "@local_config_tf//:tf_header_lib", ] + select({ @@ -735,6 +734,7 @@ cc_binary( "//tensorflow_io/core:genome_ops", "//tensorflow_io/core:optimization", "//tensorflow_io/core:oss_ops", + "//tensorflow_io/gcs:gcs_config_ops", "//tensorflow_io/core/kernels/gsmemcachedfs:gs_memcached_file_system", ], }) + select({ From 7c04ee3d6026906f28bebf14a9e245574ee6ec82 Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Mon, 14 Dec 2020 18:24:35 -0800 Subject: [PATCH 08/85] Update the API Compatibility test to include tf-nightly vs. tensorflow-io==0.17.0 (#1230) * Update the API Compatibility test to include tf-nightly vs. tensorflow-io==0.17.0 as we release tensorflow-io==0.17.0 Signed-off-by: Yong Tang * Bump Linux and Windows version checks Signed-off-by: Yong Tang --- .github/workflows/api.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/api.yml b/.github/workflows/api.yml index a601448fc..0fc774ba3 100644 --- a/.github/workflows/api.yml +++ b/.github/workflows/api.yml @@ -1,6 +1,9 @@ name: API Compatibility on: + push: + branches: + - master schedule: - cron: "0 12 * * *" @@ -15,7 +18,7 @@ jobs: strategy: matrix: python: ['3.8'] - version: ['tensorflow==2.4.0rc4:tensorflow-io-nightly', 'tf-nightly:tensorflow-io-nightly'] + version: ['tensorflow==2.4.0:tensorflow-io-nightly', 'tf-nightly:tensorflow-io==0.17.0', 'tf-nightly:tensorflow-io-nightly'] steps: - uses: actions/checkout@v2 - uses: docker-practice/actions-setup-docker@v1 @@ -54,7 +57,7 @@ jobs: strategy: matrix: python: ['3.8'] - version: ['tensorflow==2.4.0rc4:tensorflow-io-nightly', 'tf-nightly:tensorflow-io-nightly'] + version: ['tensorflow==2.4.0:tensorflow-io-nightly', 'tf-nightly:tensorflow-io==0.17.0', 'tf-nightly:tensorflow-io-nightly'] steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v1 @@ -89,7 +92,7 @@ jobs: strategy: matrix: python: ['3.8'] - version: ['tensorflow==2.4.0rc4:tensorflow-io-nightly', 'tf-nightly:tensorflow-io-nightly'] + version: ['tensorflow==2.4.0:tensorflow-io-nightly', 'tf-nightly:tensorflow-io==0.17.0', 'tf-nightly:tensorflow-io-nightly'] steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v1 From 760dd251051c1ffd0335cea9183dab012e813f0d Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Wed, 16 Dec 2020 14:35:48 -0800 Subject: [PATCH 09/85] Bump Apache Arrow to 2.0.0 (#1231) * Bump Apache Arrow to 2.0.0 Also bumps Apache Thrift to 0.13.0 Signed-off-by: Yong Tang * Update code to match Arrow Signed-off-by: Yong Tang * Bump pyarrow to 2.0.0 Signed-off-by: Yong Tang * Stay with version=1 for write_feather to pass tests Signed-off-by: Yong Tang * Bump flatbuffers to 1.12.0 Signed-off-by: Yong Tang * Fix Windows issue Signed-off-by: Yong Tang * Fix tests Signed-off-by: Yong Tang * Fix Windows Signed-off-by: Yong Tang * Remove -std=c++11 and leave default -std=c++14 for arrow build Signed-off-by: Yong Tang * Update sha256 of libapr1 As the hash changed by the repo. Signed-off-by: Yong Tang --- .github/workflows/build.wheel.sh | 2 +- WORKSPACE | 26 +- .../arrow/kernels/arrow_dataset_ops.cc | 51 +- tensorflow_io/arrow/kernels/arrow_kernels.cc | 31 +- tensorflow_io/arrow/kernels/arrow_kernels.h | 8 +- .../arrow/kernels/arrow_stream_client_unix.cc | 7 +- .../kernels/arrow_stream_client_windows.cc | 7 +- .../bigquery/kernels/bigquery_dataset_op.cc | 9 +- tensorflow_io/bigquery/kernels/bigquery_lib.h | 11 +- tensorflow_io/core/kernels/csv_kernels.cc | 25 +- tests/test_arrow_eager.py | 4 +- tests/test_feather_eager.py | 5 +- third_party/BUILD | 18 - third_party/arrow.BUILD | 17 +- third_party/parquet/parquet_types.cpp | 7348 ----------------- third_party/parquet/parquet_types.h | 2901 ------- 16 files changed, 120 insertions(+), 10350 deletions(-) delete mode 100644 third_party/parquet/parquet_types.cpp delete mode 100644 third_party/parquet/parquet_types.h diff --git a/.github/workflows/build.wheel.sh b/.github/workflows/build.wheel.sh index 1130a7cc9..78177066e 100755 --- a/.github/workflows/build.wheel.sh +++ b/.github/workflows/build.wheel.sh @@ -6,7 +6,7 @@ run_test() { entry=$1 CPYTHON_VERSION=$($entry -c 'import sys; print(str(sys.version_info[0])+str(sys.version_info[1]))') (cd wheelhouse && $entry -m pip install tensorflow_io-*-cp${CPYTHON_VERSION}-*.whl) - $entry -m pip install -q pytest pytest-benchmark boto3 fastavro avro-python3 scikit-image pandas pyarrow==0.16.0 google-cloud-pubsub==2.1.0 google-cloud-bigquery-storage==1.1.0 google-cloud-bigquery==2.3.1 google-cloud-storage==1.32.0 + $entry -m pip install -q pytest pytest-benchmark boto3 fastavro avro-python3 scikit-image pandas pyarrow==2.0.0 google-cloud-pubsub==2.1.0 google-cloud-bigquery-storage==1.1.0 google-cloud-bigquery==2.3.1 google-cloud-storage==1.32.0 (cd tests && $entry -m pytest --benchmark-disable -v --import-mode=append $(find . -type f \( -iname "test_*.py" ! \( -iname "test_*_eager.py" \) \))) (cd tests && $entry -m pytest --benchmark-disable -v --import-mode=append $(find . -type f \( -iname "test_*_eager.py" ! \( -iname "test_bigquery_eager.py" \) \))) # GRPC and test_bigquery_eager tests have to be executed separately because of https://github.com/grpc/grpc/issues/20034 diff --git a/WORKSPACE b/WORKSPACE index 907aedd40..d9fb5e3d4 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -256,22 +256,22 @@ http_archive( http_archive( name = "thrift", build_file = "//third_party:thrift.BUILD", - sha256 = "b7452d1873c6c43a580d2b4ae38cfaf8fa098ee6dc2925bae98dce0c010b1366", - strip_prefix = "thrift-0.12.0", + sha256 = "5da60088e60984f4f0801deeea628d193c33cec621e78c8a43a5d8c4055f7ad9", + strip_prefix = "thrift-0.13.0", urls = [ - "https://storage.googleapis.com/mirror.tensorflow.org/github.com/apache/thrift/archive/0.12.0.tar.gz", - "https://github.com/apache/thrift/archive/0.12.0.tar.gz", + "https://storage.googleapis.com/mirror.tensorflow.org/github.com/apache/thrift/archive/v0.13.0.tar.gz", + "https://github.com/apache/thrift/archive/v0.13.0.tar.gz", ], ) http_archive( name = "arrow", build_file = "//third_party:arrow.BUILD", - sha256 = "d7b3838758a365c8c47d55ab0df1006a70db951c6964440ba354f81f518b8d8d", - strip_prefix = "arrow-apache-arrow-0.16.0", + sha256 = "ea299df9cf440cfc43393ce12ee6d9a4c9d0dfa9fde33c3bc9b70ec25520a844", + strip_prefix = "arrow-apache-arrow-2.0.0", urls = [ - "https://storage.googleapis.com/mirror.tensorflow.org/github.com/apache/arrow/archive/apache-arrow-0.16.0.tar.gz", - "https://github.com/apache/arrow/archive/apache-arrow-0.16.0.tar.gz", + "https://storage.googleapis.com/mirror.tensorflow.org/github.com/apache/arrow/archive/apache-arrow-2.0.0.tar.gz", + "https://github.com/apache/arrow/archive/apache-arrow-2.0.0.tar.gz", ], ) @@ -429,11 +429,11 @@ http_archive( http_archive( name = "com_github_google_flatbuffers", - sha256 = "12a13686cab7ffaf8ea01711b8f55e1dbd3bf059b7c46a25fefa1250bdd9dd23", - strip_prefix = "flatbuffers-b99332efd732e6faf60bb7ce1ce5902ed65d5ba3", + sha256 = "62f2223fb9181d1d6338451375628975775f7522185266cd5296571ac152bc45", + strip_prefix = "flatbuffers-1.12.0", urls = [ - "https://storage.googleapis.com/mirror.tensorflow.org/github.com/google/flatbuffers/archive/b99332efd732e6faf60bb7ce1ce5902ed65d5ba3.tar.gz", - "https://github.com/google/flatbuffers/archive/b99332efd732e6faf60bb7ce1ce5902ed65d5ba3.tar.gz", + "https://storage.googleapis.com/mirror.tensorflow.org/github.com/google/flatbuffers/archive/v1.12.0.tar.gz", + "https://github.com/google/flatbuffers/archive/v1.12.0.tar.gz", ], ) @@ -676,7 +676,7 @@ http_archive( patches = [ "//third_party:libapr1.patch", ], - sha256 = "1a0909a1146a214a6ab9de28902045461901baab4e0ee43797539ec05b6dbae0", + sha256 = "096968a363b2374f7450a3c65f3cc0b50561204a8da7bc03a2c39e080febd6e1", strip_prefix = "apr-1.6.5", urls = [ "https://storage.googleapis.com/mirror.tensorflow.org/github.com/apache/apr/archive/1.6.5.tar.gz", diff --git a/tensorflow_io/arrow/kernels/arrow_dataset_ops.cc b/tensorflow_io/arrow/kernels/arrow_dataset_ops.cc index 3b4e41917..6ae8b1457 100644 --- a/tensorflow_io/arrow/kernels/arrow_dataset_ops.cc +++ b/tensorflow_io/arrow/kernels/arrow_dataset_ops.cc @@ -15,6 +15,7 @@ limitations under the License. #include "arrow/api.h" #include "arrow/ipc/api.h" +#include "arrow/result.h" #include "arrow/util/io_util.h" #include "tensorflow/core/framework/dataset.h" #include "tensorflow/core/graph/graph.h" @@ -476,12 +477,17 @@ class ArrowZeroCopyDatasetOp : public ArrowOpKernelBase { buffer_ = std::make_shared(dataset()->buffer_ptr_, dataset()->buffer_size_); buffer_reader_ = std::make_shared(buffer_); - CHECK_ARROW(arrow::ipc::RecordBatchFileReader::Open( - buffer_reader_.get(), buffer_->size(), &reader_)); + arrow::Result> + result = arrow::ipc::RecordBatchFileReader::Open( + buffer_reader_.get(), buffer_->size()); + CHECK_ARROW(result.status()); + reader_ = std::move(result).ValueUnsafe(); num_batches_ = reader_->num_record_batches(); if (num_batches_ > 0) { - CHECK_ARROW( - reader_->ReadRecordBatch(current_batch_idx_, ¤t_batch_)); + arrow::Result> result = + reader_->ReadRecordBatch(current_batch_idx_); + CHECK_ARROW(result.status()); + current_batch_ = std::move(result).ValueUnsafe(); TF_RETURN_IF_ERROR(CheckBatchColumnTypes(current_batch_)); } return Status::OK(); @@ -491,8 +497,10 @@ class ArrowZeroCopyDatasetOp : public ArrowOpKernelBase { TF_EXCLUSIVE_LOCKS_REQUIRED(mu_) override { ArrowBaseIterator::NextStreamLocked(env); if (++current_batch_idx_ < num_batches_) { - CHECK_ARROW( - reader_->ReadRecordBatch(current_batch_idx_, ¤t_batch_)); + arrow::Result> result = + reader_->ReadRecordBatch(current_batch_idx_); + CHECK_ARROW(result.status()); + current_batch_ = std::move(result).ValueUnsafe(); } return Status::OK(); } @@ -604,12 +612,14 @@ class ArrowSerializedDatasetOp : public ArrowOpKernelBase { const string& batches = dataset()->batches_.scalar()(); auto buffer = std::make_shared(batches); auto buffer_reader = std::make_shared(buffer); - CHECK_ARROW( - arrow::ipc::RecordBatchFileReader::Open(buffer_reader, &reader_)); + auto result = arrow::ipc::RecordBatchFileReader::Open(buffer_reader); + CHECK_ARROW(result.status()); + reader_ = std::move(result).ValueUnsafe(); num_batches_ = reader_->num_record_batches(); if (num_batches_ > 0) { - CHECK_ARROW( - reader_->ReadRecordBatch(current_batch_idx_, ¤t_batch_)); + auto result = reader_->ReadRecordBatch(current_batch_idx_); + CHECK_ARROW(result.status()); + current_batch_ = std::move(result).ValueUnsafe(); TF_RETURN_IF_ERROR(CheckBatchColumnTypes(current_batch_)); } return Status::OK(); @@ -619,8 +629,9 @@ class ArrowSerializedDatasetOp : public ArrowOpKernelBase { TF_EXCLUSIVE_LOCKS_REQUIRED(mu_) override { ArrowBaseIterator::NextStreamLocked(env); if (++current_batch_idx_ < num_batches_) { - CHECK_ARROW( - reader_->ReadRecordBatch(current_batch_idx_, ¤t_batch_)); + auto result = reader_->ReadRecordBatch(current_batch_idx_); + CHECK_ARROW(result.status()); + current_batch_ = std::move(result).ValueUnsafe(); } return Status::OK(); } @@ -736,14 +747,18 @@ class ArrowFeatherDatasetOp : public ArrowOpKernelBase { new ArrowRandomAccessFile(tf_file.get(), size)); // Create the Feather reader - std::unique_ptr reader; - CHECK_ARROW(arrow::ipc::feather::TableReader::Open(in_file, &reader)); + std::shared_ptr reader; + arrow::Result> result = + arrow::ipc::feather::Reader::Open(in_file); + CHECK_ARROW(result.status()); + reader = std::move(result).ValueUnsafe(); // Read file columns and build a table - int64_t num_columns = reader->num_columns(); std::shared_ptr<::arrow::Table> table; CHECK_ARROW(reader->Read(&table)); + int64_t num_columns = table->num_columns(); + // Convert the table to a sequence of batches arrow::TableBatchReader tr(*table.get()); std::shared_ptr batch; @@ -885,8 +900,10 @@ class ArrowStreamDatasetOp : public ArrowOpKernelBase { in_stream_ = socket_stream; } - CHECK_ARROW(arrow::ipc::RecordBatchStreamReader::Open(in_stream_.get(), - &reader_)); + auto result = + arrow::ipc::RecordBatchStreamReader::Open(in_stream_.get()); + CHECK_ARROW(result.status()); + reader_ = std::move(result).ValueUnsafe(); CHECK_ARROW(reader_->ReadNext(¤t_batch_)); TF_RETURN_IF_ERROR(CheckBatchColumnTypes(current_batch_)); return Status::OK(); diff --git a/tensorflow_io/arrow/kernels/arrow_kernels.cc b/tensorflow_io/arrow/kernels/arrow_kernels.cc index 27e64b0bf..08b4007e4 100644 --- a/tensorflow_io/arrow/kernels/arrow_kernels.cc +++ b/tensorflow_io/arrow/kernels/arrow_kernels.cc @@ -161,10 +161,11 @@ class ArrowReadableFromMemoryInitOp auto buffer_reader = std::make_shared(buffer_); std::shared_ptr schema; - arrow::Status status = - arrow::ipc::ReadSchema(buffer_reader.get(), nullptr, &schema); - OP_REQUIRES(context, status.ok(), + arrow::Result> result = + arrow::ipc::ReadSchema(buffer_reader.get(), nullptr); + OP_REQUIRES(context, result.ok(), errors::Internal("Error reading Arrow Schema")); + schema = std::move(result).ValueUnsafe(); const Tensor* array_buffer_addrs_tensor; OP_REQUIRES_OK(context, context->input("array_buffer_addresses", @@ -429,10 +430,10 @@ class ListFeatherColumnsOp : public OpKernel { ::arrow::ipc::feather::fbs::GetCTable(buffer.data()); OP_REQUIRES(context, - (table->version() >= ::arrow::ipc::feather::kFeatherVersion), + (table->version() >= ::arrow::ipc::feather::kFeatherV1Version), errors::InvalidArgument( "feather file is old: ", table->version(), " vs. ", - ::arrow::ipc::feather::kFeatherVersion)); + ::arrow::ipc::feather::kFeatherV1Version)); std::vector columns; std::vector dtypes; @@ -577,10 +578,10 @@ class FeatherReadable : public IOReadableInterface { const ::arrow::ipc::feather::fbs::CTable* table = ::arrow::ipc::feather::fbs::GetCTable(buffer.data()); - if (table->version() < ::arrow::ipc::feather::kFeatherVersion) { + if (table->version() < ::arrow::ipc::feather::kFeatherV1Version) { return errors::InvalidArgument("feather file is old: ", table->version(), " vs. ", - ::arrow::ipc::feather::kFeatherVersion); + ::arrow::ipc::feather::kFeatherV1Version); } for (size_t i = 0; i < table->columns()->size(); i++) { @@ -683,18 +684,20 @@ class FeatherReadable : public IOReadableInterface { if (feather_file_.get() == nullptr) { feather_file_.reset(new ArrowRandomAccessFile(file_.get(), file_size_)); - arrow::Status s = - arrow::ipc::feather::TableReader::Open(feather_file_, &reader_); - if (!s.ok()) { - return errors::Internal(s.ToString()); + arrow::Result> result = + arrow::ipc::feather::Reader::Open(feather_file_); + if (!result.ok()) { + return errors::Internal(result.status().ToString()); } + reader_ = std::move(result).ValueUnsafe(); } - std::shared_ptr column; - arrow::Status s = reader_->GetColumn(column_index, &column); + std::shared_ptr table; + arrow::Status s = reader_->Read(&table); if (!s.ok()) { return errors::Internal(s.ToString()); } + std::shared_ptr column = table->column(column_index); std::shared_ptr<::arrow::ChunkedArray> slice = column->Slice(element_start, element_stop); @@ -767,7 +770,7 @@ class FeatherReadable : public IOReadableInterface { std::unique_ptr file_ TF_GUARDED_BY(mu_); uint64 file_size_ TF_GUARDED_BY(mu_); std::shared_ptr feather_file_ TF_GUARDED_BY(mu_); - std::unique_ptr reader_ TF_GUARDED_BY(mu_); + std::shared_ptr reader_ TF_GUARDED_BY(mu_); std::vector dtypes_; std::vector shapes_; diff --git a/tensorflow_io/arrow/kernels/arrow_kernels.h b/tensorflow_io/arrow/kernels/arrow_kernels.h index 2ad9e57b5..4a6b88e43 100644 --- a/tensorflow_io/arrow/kernels/arrow_kernels.h +++ b/tensorflow_io/arrow/kernels/arrow_kernels.h @@ -51,8 +51,12 @@ class ArrowRandomAccessFile : public ::arrow::io::RandomAccessFile { return result.size(); } arrow::Result> Read(int64_t nbytes) override { - std::shared_ptr buffer; - RETURN_NOT_OK(AllocateResizableBuffer(nbytes, &buffer)); + arrow::Result> result = + arrow::AllocateResizableBuffer(nbytes); + ARROW_RETURN_NOT_OK(result); + std::shared_ptr buffer = + std::move(result).ValueUnsafe(); + ARROW_ASSIGN_OR_RAISE(int64_t bytes_read, Read(nbytes, buffer->mutable_data())); RETURN_NOT_OK(buffer->Resize(bytes_read)); diff --git a/tensorflow_io/arrow/kernels/arrow_stream_client_unix.cc b/tensorflow_io/arrow/kernels/arrow_stream_client_unix.cc index 2bc3d0f2d..c685eb14c 100644 --- a/tensorflow_io/arrow/kernels/arrow_stream_client_unix.cc +++ b/tensorflow_io/arrow/kernels/arrow_stream_client_unix.cc @@ -132,8 +132,11 @@ arrow::Result ArrowStreamClient::Read(int64_t nbytes, void* out) { arrow::Result> ArrowStreamClient::Read( int64_t nbytes) { - std::shared_ptr buffer; - ARROW_RETURN_NOT_OK(arrow::AllocateResizableBuffer(nbytes, &buffer)); + arrow::Result> result = + arrow::AllocateResizableBuffer(nbytes); + ARROW_RETURN_NOT_OK(result); + std::shared_ptr buffer = + std::move(result).ValueUnsafe(); int64_t bytes_read; ARROW_ASSIGN_OR_RAISE(bytes_read, Read(nbytes, buffer->mutable_data())); ARROW_RETURN_NOT_OK(buffer->Resize(bytes_read, false)); diff --git a/tensorflow_io/arrow/kernels/arrow_stream_client_windows.cc b/tensorflow_io/arrow/kernels/arrow_stream_client_windows.cc index a1c9cd641..0bc1fdacd 100644 --- a/tensorflow_io/arrow/kernels/arrow_stream_client_windows.cc +++ b/tensorflow_io/arrow/kernels/arrow_stream_client_windows.cc @@ -154,8 +154,11 @@ arrow::Result ArrowStreamClient::Read(int64_t nbytes, void* out) { arrow::Result> ArrowStreamClient::Read( int64_t nbytes) { - std::shared_ptr buffer; - ARROW_RETURN_NOT_OK(arrow::AllocateResizableBuffer(nbytes, &buffer)); + arrow::Result> result = + arrow::AllocateResizableBuffer(nbytes); + ARROW_RETURN_NOT_OK(result); + std::shared_ptr buffer = + std::move(result).ValueUnsafe(); int64_t bytes_read; ARROW_ASSIGN_OR_RAISE(bytes_read, Read(nbytes, buffer->mutable_data())); ARROW_RETURN_NOT_OK(buffer->Resize(bytes_read, false)); diff --git a/tensorflow_io/bigquery/kernels/bigquery_dataset_op.cc b/tensorflow_io/bigquery/kernels/bigquery_dataset_op.cc index a6253de99..66a228235 100644 --- a/tensorflow_io/bigquery/kernels/bigquery_dataset_op.cc +++ b/tensorflow_io/bigquery/kernels/bigquery_dataset_op.cc @@ -102,11 +102,12 @@ class BigQueryDatasetOp : public DatasetOpKernel { arrow::ipc::DictionaryMemo dict_memo; arrow::io::BufferReader input(buffer_); - arrow::Status arrow_status = - arrow::ipc::ReadSchema(&input, &dict_memo, &arrow_schema_); - OP_REQUIRES(ctx, arrow_status.ok(), + arrow::Result> result = + arrow::ipc::ReadSchema(&input, &dict_memo); + OP_REQUIRES(ctx, result.ok(), errors::Internal("Error reading Arrow Schema", - arrow_status.message())); + result.status().message())); + arrow_schema_ = std::move(result).ValueUnsafe(); } else { ctx->CtxFailure(errors::InvalidArgument("Invalid data_format")); } diff --git a/tensorflow_io/bigquery/kernels/bigquery_lib.h b/tensorflow_io/bigquery/kernels/bigquery_lib.h index 3164824ea..0e6914077 100644 --- a/tensorflow_io/bigquery/kernels/bigquery_lib.h +++ b/tensorflow_io/bigquery/kernels/bigquery_lib.h @@ -224,12 +224,13 @@ class BigQueryReaderArrowDatasetIterator arrow::io::BufferReader buffer_reader_(buffer_); arrow::ipc::DictionaryMemo dict_memo; - auto arrow_status = - arrow::ipc::ReadRecordBatch(this->dataset()->arrow_schema(), &dict_memo, - &buffer_reader_, &this->record_batch_); - if (!arrow_status.ok()) { - return errors::Internal(arrow_status.ToString()); + auto result = arrow::ipc::ReadRecordBatch( + this->dataset()->arrow_schema(), &dict_memo, + arrow::ipc::IpcReadOptions::Defaults(), &buffer_reader_); + if (!result.ok()) { + return errors::Internal(result.status().ToString()); } + this->record_batch_ = std::move(result).ValueUnsafe(); VLOG(3) << "got record batch, rows:" << record_batch_->num_rows(); diff --git a/tensorflow_io/core/kernels/csv_kernels.cc b/tensorflow_io/core/kernels/csv_kernels.cc index c2d5c4f5e..196d1a31c 100644 --- a/tensorflow_io/core/kernels/csv_kernels.cc +++ b/tensorflow_io/core/kernels/csv_kernels.cc @@ -44,19 +44,24 @@ class CSVReadable : public IOReadableInterface { csv_file_.reset(new ArrowRandomAccessFile(file_.get(), file_size_)); - ::arrow::Status status; - - status = ::arrow::csv::TableReader::Make( + auto result = ::arrow::csv::TableReader::Make( ::arrow::default_memory_pool(), csv_file_, ::arrow::csv::ReadOptions::Defaults(), ::arrow::csv::ParseOptions::Defaults(), - ::arrow::csv::ConvertOptions::Defaults(), &reader_); - if (!status.ok()) { - return errors::InvalidArgument("unable to make a TableReader: ", status); + ::arrow::csv::ConvertOptions::Defaults()); + if (!result.status().ok()) { + return errors::InvalidArgument("unable to make a TableReader: ", + result.status()); } - status = reader_->Read(&table_); - if (!status.ok()) { - return errors::InvalidArgument("unable to read table: ", status); + reader_ = std::move(result).ValueUnsafe(); + + { + auto result = reader_->Read(); + if (!result.status().ok()) { + return errors::InvalidArgument("unable to read table: ", + result.status()); + } + table_ = std::move(result).ValueUnsafe(); } for (int i = 0; i < table_->num_columns(); i++) { @@ -108,11 +113,9 @@ class CSVReadable : public IOReadableInterface { case ::arrow::Type::TIMESTAMP: case ::arrow::Type::TIME32: case ::arrow::Type::TIME64: - case ::arrow::Type::INTERVAL: case ::arrow::Type::DECIMAL: case ::arrow::Type::LIST: case ::arrow::Type::STRUCT: - case ::arrow::Type::UNION: case ::arrow::Type::DICTIONARY: case ::arrow::Type::MAP: default: diff --git a/tests/test_arrow_eager.py b/tests/test_arrow_eager.py index a744c6696..9c4d38c6b 100644 --- a/tests/test_arrow_eager.py +++ b/tests/test_arrow_eager.py @@ -508,7 +508,7 @@ def test_arrow_feather_dataset(self): # Create a tempfile that is deleted after tests run with tempfile.NamedTemporaryFile(delete=False) as f: - write_feather(df, f) + write_feather(df, f, version=1) # test single file dataset = arrow_io.ArrowFeatherDataset( @@ -1143,7 +1143,7 @@ def test_arrow_list_feather_columns(self): # Create a tempfile that is deleted after tests run with tempfile.NamedTemporaryFile(delete=False) as f: - write_feather(df, f) + write_feather(df, f, version=1) # test single file # prefix "file://" to test scheme file system (e.g., s3, gcs, azfs, ignite) diff --git a/tests/test_feather_eager.py b/tests/test_feather_eager.py index fae30c5b0..5bbdcd2d3 100644 --- a/tests/test_feather_eager.py +++ b/tests/test_feather_eager.py @@ -20,6 +20,7 @@ import numpy as np import pandas as pd +import pyarrow as pa import tensorflow_io as tfio # pylint: disable=wrong-import-position @@ -37,9 +38,7 @@ def test_feather_format(): } df = pd.DataFrame(data).sort_index(axis=1) with tempfile.NamedTemporaryFile(delete=False) as f: - df.to_feather(f) - - df = pd.read_feather(f.name) + pa.feather.write_feather(df, f, version=1) feather = tfio.IOTensor.from_feather(f.name) for column in df.columns: diff --git a/third_party/BUILD b/third_party/BUILD index f364c8be7..7b8d4c583 100644 --- a/third_party/BUILD +++ b/third_party/BUILD @@ -28,21 +28,3 @@ cc_library( visibility = ["//visibility:public"], deps = [], ) - -# parquet_types.[h|cpp] are generated from apache arrow 0.16.0 -# on Ubuntu 16.04 with default gcc/g++/cmake/flex/bison. -cc_library( - name = "parquet", - srcs = [ - "parquet/parquet_types.h", - ], - hdrs = [], - copts = [], - includes = ["."], - visibility = ["//visibility:public"], - deps = [], -) - -exports_files([ - "parquet/parquet_types.cpp", -]) diff --git a/third_party/arrow.BUILD b/third_party/arrow.BUILD index b60118cee..fff11cd50 100644 --- a/third_party/arrow.BUILD +++ b/third_party/arrow.BUILD @@ -31,8 +31,8 @@ genrule( srcs = ["cpp/src/arrow/util/config.h.cmake"], outs = ["cpp/src/arrow/util/config.h"], cmd = ("sed " + - "-e 's/@ARROW_VERSION_MAJOR@/0/g' " + - "-e 's/@ARROW_VERSION_MINOR@/16/g' " + + "-e 's/@ARROW_VERSION_MAJOR@/2/g' " + + "-e 's/@ARROW_VERSION_MINOR@/0/g' " + "-e 's/@ARROW_VERSION_PATCH@/0/g' " + "-e 's/cmakedefine/define/g' " + "$< >$@"), @@ -59,13 +59,17 @@ cc_library( "cpp/src/arrow/io/*.cc", "cpp/src/arrow/ipc/*.cc", "cpp/src/arrow/json/*.cc", + "cpp/src/arrow/tensor/*.cc", "cpp/src/arrow/util/*.cc", + "cpp/src/arrow/vendored/musl/strptime.c", "cpp/src/arrow/vendored/optional.hpp", "cpp/src/arrow/vendored/string_view.hpp", "cpp/src/arrow/vendored/variant.hpp", "cpp/src/arrow/**/*.h", "cpp/src/parquet/**/*.h", "cpp/src/parquet/**/*.cc", + "cpp/src/generated/*.h", + "cpp/src/generated/*.cpp", ], exclude = [ "cpp/src/**/*_benchmark.cc", @@ -77,16 +81,16 @@ cc_library( "cpp/src/**/*fuzz*.cc", "cpp/src/**/file_to_stream.cc", "cpp/src/**/stream_to_file.cc", + "cpp/src/arrow/util/bpacking_avx2.cc", + "cpp/src/arrow/util/bpacking_avx512.cc", ], - ) + [ - "@org_tensorflow_io//third_party:parquet/parquet_types.cpp", - ], + ), hdrs = [ # declare header from above genrule "cpp/src/arrow/util/config.h", "cpp/src/parquet/parquet_version.h", ], - copts = ["-std=c++11"], + copts = [], defines = [ "ARROW_WITH_BROTLI", "ARROW_WITH_SNAPPY", @@ -112,7 +116,6 @@ cc_library( "@bzip2", "@double-conversion", "@lz4", - "@org_tensorflow_io//third_party:parquet", "@rapidjson", "@snappy", "@thrift", diff --git a/third_party/parquet/parquet_types.cpp b/third_party/parquet/parquet_types.cpp deleted file mode 100644 index 327f75a1b..000000000 --- a/third_party/parquet/parquet_types.cpp +++ /dev/null @@ -1,7348 +0,0 @@ -/** - * Autogenerated by Thrift Compiler (0.12.0) - * - * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING - * @generated - */ -#include "parquet_types.h" - -#include -#include - -#include - -namespace parquet { namespace format { - -int _kTypeValues[] = { - Type::BOOLEAN, - Type::INT32, - Type::INT64, - Type::INT96, - Type::FLOAT, - Type::DOUBLE, - Type::BYTE_ARRAY, - Type::FIXED_LEN_BYTE_ARRAY -}; -const char* _kTypeNames[] = { - "BOOLEAN", - "INT32", - "INT64", - "INT96", - "FLOAT", - "DOUBLE", - "BYTE_ARRAY", - "FIXED_LEN_BYTE_ARRAY" -}; -const std::map _Type_VALUES_TO_NAMES(::apache::thrift::TEnumIterator(8, _kTypeValues, _kTypeNames), ::apache::thrift::TEnumIterator(-1, NULL, NULL)); - -std::ostream& operator<<(std::ostream& out, const Type::type& val) { - std::map::const_iterator it = _Type_VALUES_TO_NAMES.find(val); - if (it != _Type_VALUES_TO_NAMES.end()) { - out << it->second; - } else { - out << static_cast(val); - } - return out; -} - -int _kConvertedTypeValues[] = { - ConvertedType::UTF8, - ConvertedType::MAP, - ConvertedType::MAP_KEY_VALUE, - ConvertedType::LIST, - ConvertedType::ENUM, - ConvertedType::DECIMAL, - ConvertedType::DATE, - ConvertedType::TIME_MILLIS, - ConvertedType::TIME_MICROS, - ConvertedType::TIMESTAMP_MILLIS, - ConvertedType::TIMESTAMP_MICROS, - ConvertedType::UINT_8, - ConvertedType::UINT_16, - ConvertedType::UINT_32, - ConvertedType::UINT_64, - ConvertedType::INT_8, - ConvertedType::INT_16, - ConvertedType::INT_32, - ConvertedType::INT_64, - ConvertedType::JSON, - ConvertedType::BSON, - ConvertedType::INTERVAL -}; -const char* _kConvertedTypeNames[] = { - "UTF8", - "MAP", - "MAP_KEY_VALUE", - "LIST", - "ENUM", - "DECIMAL", - "DATE", - "TIME_MILLIS", - "TIME_MICROS", - "TIMESTAMP_MILLIS", - "TIMESTAMP_MICROS", - "UINT_8", - "UINT_16", - "UINT_32", - "UINT_64", - "INT_8", - "INT_16", - "INT_32", - "INT_64", - "JSON", - "BSON", - "INTERVAL" -}; -const std::map _ConvertedType_VALUES_TO_NAMES(::apache::thrift::TEnumIterator(22, _kConvertedTypeValues, _kConvertedTypeNames), ::apache::thrift::TEnumIterator(-1, NULL, NULL)); - -std::ostream& operator<<(std::ostream& out, const ConvertedType::type& val) { - std::map::const_iterator it = _ConvertedType_VALUES_TO_NAMES.find(val); - if (it != _ConvertedType_VALUES_TO_NAMES.end()) { - out << it->second; - } else { - out << static_cast(val); - } - return out; -} - -int _kFieldRepetitionTypeValues[] = { - FieldRepetitionType::REQUIRED, - FieldRepetitionType::OPTIONAL, - FieldRepetitionType::REPEATED -}; -const char* _kFieldRepetitionTypeNames[] = { - "REQUIRED", - "OPTIONAL", - "REPEATED" -}; -const std::map _FieldRepetitionType_VALUES_TO_NAMES(::apache::thrift::TEnumIterator(3, _kFieldRepetitionTypeValues, _kFieldRepetitionTypeNames), ::apache::thrift::TEnumIterator(-1, NULL, NULL)); - -std::ostream& operator<<(std::ostream& out, const FieldRepetitionType::type& val) { - std::map::const_iterator it = _FieldRepetitionType_VALUES_TO_NAMES.find(val); - if (it != _FieldRepetitionType_VALUES_TO_NAMES.end()) { - out << it->second; - } else { - out << static_cast(val); - } - return out; -} - -int _kEncodingValues[] = { - Encoding::PLAIN, - Encoding::PLAIN_DICTIONARY, - Encoding::RLE, - Encoding::BIT_PACKED, - Encoding::DELTA_BINARY_PACKED, - Encoding::DELTA_LENGTH_BYTE_ARRAY, - Encoding::DELTA_BYTE_ARRAY, - Encoding::RLE_DICTIONARY, - Encoding::BYTE_STREAM_SPLIT -}; -const char* _kEncodingNames[] = { - "PLAIN", - "PLAIN_DICTIONARY", - "RLE", - "BIT_PACKED", - "DELTA_BINARY_PACKED", - "DELTA_LENGTH_BYTE_ARRAY", - "DELTA_BYTE_ARRAY", - "RLE_DICTIONARY", - "BYTE_STREAM_SPLIT" -}; -const std::map _Encoding_VALUES_TO_NAMES(::apache::thrift::TEnumIterator(9, _kEncodingValues, _kEncodingNames), ::apache::thrift::TEnumIterator(-1, NULL, NULL)); - -std::ostream& operator<<(std::ostream& out, const Encoding::type& val) { - std::map::const_iterator it = _Encoding_VALUES_TO_NAMES.find(val); - if (it != _Encoding_VALUES_TO_NAMES.end()) { - out << it->second; - } else { - out << static_cast(val); - } - return out; -} - -int _kCompressionCodecValues[] = { - CompressionCodec::UNCOMPRESSED, - CompressionCodec::SNAPPY, - CompressionCodec::GZIP, - CompressionCodec::LZO, - CompressionCodec::BROTLI, - CompressionCodec::LZ4, - CompressionCodec::ZSTD -}; -const char* _kCompressionCodecNames[] = { - "UNCOMPRESSED", - "SNAPPY", - "GZIP", - "LZO", - "BROTLI", - "LZ4", - "ZSTD" -}; -const std::map _CompressionCodec_VALUES_TO_NAMES(::apache::thrift::TEnumIterator(7, _kCompressionCodecValues, _kCompressionCodecNames), ::apache::thrift::TEnumIterator(-1, NULL, NULL)); - -std::ostream& operator<<(std::ostream& out, const CompressionCodec::type& val) { - std::map::const_iterator it = _CompressionCodec_VALUES_TO_NAMES.find(val); - if (it != _CompressionCodec_VALUES_TO_NAMES.end()) { - out << it->second; - } else { - out << static_cast(val); - } - return out; -} - -int _kPageTypeValues[] = { - PageType::DATA_PAGE, - PageType::INDEX_PAGE, - PageType::DICTIONARY_PAGE, - PageType::DATA_PAGE_V2 -}; -const char* _kPageTypeNames[] = { - "DATA_PAGE", - "INDEX_PAGE", - "DICTIONARY_PAGE", - "DATA_PAGE_V2" -}; -const std::map _PageType_VALUES_TO_NAMES(::apache::thrift::TEnumIterator(4, _kPageTypeValues, _kPageTypeNames), ::apache::thrift::TEnumIterator(-1, NULL, NULL)); - -std::ostream& operator<<(std::ostream& out, const PageType::type& val) { - std::map::const_iterator it = _PageType_VALUES_TO_NAMES.find(val); - if (it != _PageType_VALUES_TO_NAMES.end()) { - out << it->second; - } else { - out << static_cast(val); - } - return out; -} - -int _kBoundaryOrderValues[] = { - BoundaryOrder::UNORDERED, - BoundaryOrder::ASCENDING, - BoundaryOrder::DESCENDING -}; -const char* _kBoundaryOrderNames[] = { - "UNORDERED", - "ASCENDING", - "DESCENDING" -}; -const std::map _BoundaryOrder_VALUES_TO_NAMES(::apache::thrift::TEnumIterator(3, _kBoundaryOrderValues, _kBoundaryOrderNames), ::apache::thrift::TEnumIterator(-1, NULL, NULL)); - -std::ostream& operator<<(std::ostream& out, const BoundaryOrder::type& val) { - std::map::const_iterator it = _BoundaryOrder_VALUES_TO_NAMES.find(val); - if (it != _BoundaryOrder_VALUES_TO_NAMES.end()) { - out << it->second; - } else { - out << static_cast(val); - } - return out; -} - - -Statistics::~Statistics() throw() { -} - - -void Statistics::__set_max(const std::string& val) { - this->max = val; -__isset.max = true; -} - -void Statistics::__set_min(const std::string& val) { - this->min = val; -__isset.min = true; -} - -void Statistics::__set_null_count(const int64_t val) { - this->null_count = val; -__isset.null_count = true; -} - -void Statistics::__set_distinct_count(const int64_t val) { - this->distinct_count = val; -__isset.distinct_count = true; -} - -void Statistics::__set_max_value(const std::string& val) { - this->max_value = val; -__isset.max_value = true; -} - -void Statistics::__set_min_value(const std::string& val) { - this->min_value = val; -__isset.min_value = true; -} -std::ostream& operator<<(std::ostream& out, const Statistics& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t Statistics::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - switch (fid) - { - case 1: - if (ftype == ::apache::thrift::protocol::T_STRING) { - xfer += iprot->readBinary(this->max); - this->__isset.max = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 2: - if (ftype == ::apache::thrift::protocol::T_STRING) { - xfer += iprot->readBinary(this->min); - this->__isset.min = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 3: - if (ftype == ::apache::thrift::protocol::T_I64) { - xfer += iprot->readI64(this->null_count); - this->__isset.null_count = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 4: - if (ftype == ::apache::thrift::protocol::T_I64) { - xfer += iprot->readI64(this->distinct_count); - this->__isset.distinct_count = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 5: - if (ftype == ::apache::thrift::protocol::T_STRING) { - xfer += iprot->readBinary(this->max_value); - this->__isset.max_value = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 6: - if (ftype == ::apache::thrift::protocol::T_STRING) { - xfer += iprot->readBinary(this->min_value); - this->__isset.min_value = true; - } else { - xfer += iprot->skip(ftype); - } - break; - default: - xfer += iprot->skip(ftype); - break; - } - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - return xfer; -} - -uint32_t Statistics::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("Statistics"); - - if (this->__isset.max) { - xfer += oprot->writeFieldBegin("max", ::apache::thrift::protocol::T_STRING, 1); - xfer += oprot->writeBinary(this->max); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.min) { - xfer += oprot->writeFieldBegin("min", ::apache::thrift::protocol::T_STRING, 2); - xfer += oprot->writeBinary(this->min); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.null_count) { - xfer += oprot->writeFieldBegin("null_count", ::apache::thrift::protocol::T_I64, 3); - xfer += oprot->writeI64(this->null_count); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.distinct_count) { - xfer += oprot->writeFieldBegin("distinct_count", ::apache::thrift::protocol::T_I64, 4); - xfer += oprot->writeI64(this->distinct_count); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.max_value) { - xfer += oprot->writeFieldBegin("max_value", ::apache::thrift::protocol::T_STRING, 5); - xfer += oprot->writeBinary(this->max_value); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.min_value) { - xfer += oprot->writeFieldBegin("min_value", ::apache::thrift::protocol::T_STRING, 6); - xfer += oprot->writeBinary(this->min_value); - xfer += oprot->writeFieldEnd(); - } - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(Statistics &a, Statistics &b) { - using ::std::swap; - swap(a.max, b.max); - swap(a.min, b.min); - swap(a.null_count, b.null_count); - swap(a.distinct_count, b.distinct_count); - swap(a.max_value, b.max_value); - swap(a.min_value, b.min_value); - swap(a.__isset, b.__isset); -} - -Statistics::Statistics(const Statistics& other0) { - max = other0.max; - min = other0.min; - null_count = other0.null_count; - distinct_count = other0.distinct_count; - max_value = other0.max_value; - min_value = other0.min_value; - __isset = other0.__isset; -} -Statistics& Statistics::operator=(const Statistics& other1) { - max = other1.max; - min = other1.min; - null_count = other1.null_count; - distinct_count = other1.distinct_count; - max_value = other1.max_value; - min_value = other1.min_value; - __isset = other1.__isset; - return *this; -} -void Statistics::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "Statistics("; - out << "max="; (__isset.max ? (out << to_string(max)) : (out << "")); - out << ", " << "min="; (__isset.min ? (out << to_string(min)) : (out << "")); - out << ", " << "null_count="; (__isset.null_count ? (out << to_string(null_count)) : (out << "")); - out << ", " << "distinct_count="; (__isset.distinct_count ? (out << to_string(distinct_count)) : (out << "")); - out << ", " << "max_value="; (__isset.max_value ? (out << to_string(max_value)) : (out << "")); - out << ", " << "min_value="; (__isset.min_value ? (out << to_string(min_value)) : (out << "")); - out << ")"; -} - - -StringType::~StringType() throw() { -} - -std::ostream& operator<<(std::ostream& out, const StringType& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t StringType::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - xfer += iprot->skip(ftype); - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - return xfer; -} - -uint32_t StringType::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("StringType"); - - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(StringType &a, StringType &b) { - using ::std::swap; - (void) a; - (void) b; -} - -StringType::StringType(const StringType& other2) { - (void) other2; -} -StringType& StringType::operator=(const StringType& other3) { - (void) other3; - return *this; -} -void StringType::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "StringType("; - out << ")"; -} - - -UUIDType::~UUIDType() throw() { -} - -std::ostream& operator<<(std::ostream& out, const UUIDType& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t UUIDType::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - xfer += iprot->skip(ftype); - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - return xfer; -} - -uint32_t UUIDType::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("UUIDType"); - - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(UUIDType &a, UUIDType &b) { - using ::std::swap; - (void) a; - (void) b; -} - -UUIDType::UUIDType(const UUIDType& other4) { - (void) other4; -} -UUIDType& UUIDType::operator=(const UUIDType& other5) { - (void) other5; - return *this; -} -void UUIDType::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "UUIDType("; - out << ")"; -} - - -MapType::~MapType() throw() { -} - -std::ostream& operator<<(std::ostream& out, const MapType& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t MapType::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - xfer += iprot->skip(ftype); - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - return xfer; -} - -uint32_t MapType::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("MapType"); - - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(MapType &a, MapType &b) { - using ::std::swap; - (void) a; - (void) b; -} - -MapType::MapType(const MapType& other6) { - (void) other6; -} -MapType& MapType::operator=(const MapType& other7) { - (void) other7; - return *this; -} -void MapType::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "MapType("; - out << ")"; -} - - -ListType::~ListType() throw() { -} - -std::ostream& operator<<(std::ostream& out, const ListType& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t ListType::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - xfer += iprot->skip(ftype); - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - return xfer; -} - -uint32_t ListType::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("ListType"); - - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(ListType &a, ListType &b) { - using ::std::swap; - (void) a; - (void) b; -} - -ListType::ListType(const ListType& other8) { - (void) other8; -} -ListType& ListType::operator=(const ListType& other9) { - (void) other9; - return *this; -} -void ListType::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "ListType("; - out << ")"; -} - - -EnumType::~EnumType() throw() { -} - -std::ostream& operator<<(std::ostream& out, const EnumType& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t EnumType::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - xfer += iprot->skip(ftype); - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - return xfer; -} - -uint32_t EnumType::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("EnumType"); - - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(EnumType &a, EnumType &b) { - using ::std::swap; - (void) a; - (void) b; -} - -EnumType::EnumType(const EnumType& other10) { - (void) other10; -} -EnumType& EnumType::operator=(const EnumType& other11) { - (void) other11; - return *this; -} -void EnumType::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "EnumType("; - out << ")"; -} - - -DateType::~DateType() throw() { -} - -std::ostream& operator<<(std::ostream& out, const DateType& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t DateType::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - xfer += iprot->skip(ftype); - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - return xfer; -} - -uint32_t DateType::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("DateType"); - - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(DateType &a, DateType &b) { - using ::std::swap; - (void) a; - (void) b; -} - -DateType::DateType(const DateType& other12) { - (void) other12; -} -DateType& DateType::operator=(const DateType& other13) { - (void) other13; - return *this; -} -void DateType::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "DateType("; - out << ")"; -} - - -NullType::~NullType() throw() { -} - -std::ostream& operator<<(std::ostream& out, const NullType& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t NullType::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - xfer += iprot->skip(ftype); - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - return xfer; -} - -uint32_t NullType::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("NullType"); - - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(NullType &a, NullType &b) { - using ::std::swap; - (void) a; - (void) b; -} - -NullType::NullType(const NullType& other14) { - (void) other14; -} -NullType& NullType::operator=(const NullType& other15) { - (void) other15; - return *this; -} -void NullType::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "NullType("; - out << ")"; -} - - -DecimalType::~DecimalType() throw() { -} - - -void DecimalType::__set_scale(const int32_t val) { - this->scale = val; -} - -void DecimalType::__set_precision(const int32_t val) { - this->precision = val; -} -std::ostream& operator<<(std::ostream& out, const DecimalType& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t DecimalType::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - bool isset_scale = false; - bool isset_precision = false; - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - switch (fid) - { - case 1: - if (ftype == ::apache::thrift::protocol::T_I32) { - xfer += iprot->readI32(this->scale); - isset_scale = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 2: - if (ftype == ::apache::thrift::protocol::T_I32) { - xfer += iprot->readI32(this->precision); - isset_precision = true; - } else { - xfer += iprot->skip(ftype); - } - break; - default: - xfer += iprot->skip(ftype); - break; - } - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - if (!isset_scale) - throw TProtocolException(TProtocolException::INVALID_DATA); - if (!isset_precision) - throw TProtocolException(TProtocolException::INVALID_DATA); - return xfer; -} - -uint32_t DecimalType::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("DecimalType"); - - xfer += oprot->writeFieldBegin("scale", ::apache::thrift::protocol::T_I32, 1); - xfer += oprot->writeI32(this->scale); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldBegin("precision", ::apache::thrift::protocol::T_I32, 2); - xfer += oprot->writeI32(this->precision); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(DecimalType &a, DecimalType &b) { - using ::std::swap; - swap(a.scale, b.scale); - swap(a.precision, b.precision); -} - -DecimalType::DecimalType(const DecimalType& other16) { - scale = other16.scale; - precision = other16.precision; -} -DecimalType& DecimalType::operator=(const DecimalType& other17) { - scale = other17.scale; - precision = other17.precision; - return *this; -} -void DecimalType::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "DecimalType("; - out << "scale=" << to_string(scale); - out << ", " << "precision=" << to_string(precision); - out << ")"; -} - - -MilliSeconds::~MilliSeconds() throw() { -} - -std::ostream& operator<<(std::ostream& out, const MilliSeconds& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t MilliSeconds::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - xfer += iprot->skip(ftype); - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - return xfer; -} - -uint32_t MilliSeconds::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("MilliSeconds"); - - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(MilliSeconds &a, MilliSeconds &b) { - using ::std::swap; - (void) a; - (void) b; -} - -MilliSeconds::MilliSeconds(const MilliSeconds& other18) { - (void) other18; -} -MilliSeconds& MilliSeconds::operator=(const MilliSeconds& other19) { - (void) other19; - return *this; -} -void MilliSeconds::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "MilliSeconds("; - out << ")"; -} - - -MicroSeconds::~MicroSeconds() throw() { -} - -std::ostream& operator<<(std::ostream& out, const MicroSeconds& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t MicroSeconds::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - xfer += iprot->skip(ftype); - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - return xfer; -} - -uint32_t MicroSeconds::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("MicroSeconds"); - - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(MicroSeconds &a, MicroSeconds &b) { - using ::std::swap; - (void) a; - (void) b; -} - -MicroSeconds::MicroSeconds(const MicroSeconds& other20) { - (void) other20; -} -MicroSeconds& MicroSeconds::operator=(const MicroSeconds& other21) { - (void) other21; - return *this; -} -void MicroSeconds::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "MicroSeconds("; - out << ")"; -} - - -NanoSeconds::~NanoSeconds() throw() { -} - -std::ostream& operator<<(std::ostream& out, const NanoSeconds& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t NanoSeconds::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - xfer += iprot->skip(ftype); - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - return xfer; -} - -uint32_t NanoSeconds::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("NanoSeconds"); - - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(NanoSeconds &a, NanoSeconds &b) { - using ::std::swap; - (void) a; - (void) b; -} - -NanoSeconds::NanoSeconds(const NanoSeconds& other22) { - (void) other22; -} -NanoSeconds& NanoSeconds::operator=(const NanoSeconds& other23) { - (void) other23; - return *this; -} -void NanoSeconds::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "NanoSeconds("; - out << ")"; -} - - -TimeUnit::~TimeUnit() throw() { -} - - -void TimeUnit::__set_MILLIS(const MilliSeconds& val) { - this->MILLIS = val; -__isset.MILLIS = true; -} - -void TimeUnit::__set_MICROS(const MicroSeconds& val) { - this->MICROS = val; -__isset.MICROS = true; -} - -void TimeUnit::__set_NANOS(const NanoSeconds& val) { - this->NANOS = val; -__isset.NANOS = true; -} -std::ostream& operator<<(std::ostream& out, const TimeUnit& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t TimeUnit::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - switch (fid) - { - case 1: - if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->MILLIS.read(iprot); - this->__isset.MILLIS = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 2: - if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->MICROS.read(iprot); - this->__isset.MICROS = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 3: - if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->NANOS.read(iprot); - this->__isset.NANOS = true; - } else { - xfer += iprot->skip(ftype); - } - break; - default: - xfer += iprot->skip(ftype); - break; - } - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - return xfer; -} - -uint32_t TimeUnit::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("TimeUnit"); - - if (this->__isset.MILLIS) { - xfer += oprot->writeFieldBegin("MILLIS", ::apache::thrift::protocol::T_STRUCT, 1); - xfer += this->MILLIS.write(oprot); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.MICROS) { - xfer += oprot->writeFieldBegin("MICROS", ::apache::thrift::protocol::T_STRUCT, 2); - xfer += this->MICROS.write(oprot); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.NANOS) { - xfer += oprot->writeFieldBegin("NANOS", ::apache::thrift::protocol::T_STRUCT, 3); - xfer += this->NANOS.write(oprot); - xfer += oprot->writeFieldEnd(); - } - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(TimeUnit &a, TimeUnit &b) { - using ::std::swap; - swap(a.MILLIS, b.MILLIS); - swap(a.MICROS, b.MICROS); - swap(a.NANOS, b.NANOS); - swap(a.__isset, b.__isset); -} - -TimeUnit::TimeUnit(const TimeUnit& other24) { - MILLIS = other24.MILLIS; - MICROS = other24.MICROS; - NANOS = other24.NANOS; - __isset = other24.__isset; -} -TimeUnit& TimeUnit::operator=(const TimeUnit& other25) { - MILLIS = other25.MILLIS; - MICROS = other25.MICROS; - NANOS = other25.NANOS; - __isset = other25.__isset; - return *this; -} -void TimeUnit::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "TimeUnit("; - out << "MILLIS="; (__isset.MILLIS ? (out << to_string(MILLIS)) : (out << "")); - out << ", " << "MICROS="; (__isset.MICROS ? (out << to_string(MICROS)) : (out << "")); - out << ", " << "NANOS="; (__isset.NANOS ? (out << to_string(NANOS)) : (out << "")); - out << ")"; -} - - -TimestampType::~TimestampType() throw() { -} - - -void TimestampType::__set_isAdjustedToUTC(const bool val) { - this->isAdjustedToUTC = val; -} - -void TimestampType::__set_unit(const TimeUnit& val) { - this->unit = val; -} -std::ostream& operator<<(std::ostream& out, const TimestampType& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t TimestampType::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - bool isset_isAdjustedToUTC = false; - bool isset_unit = false; - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - switch (fid) - { - case 1: - if (ftype == ::apache::thrift::protocol::T_BOOL) { - xfer += iprot->readBool(this->isAdjustedToUTC); - isset_isAdjustedToUTC = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 2: - if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->unit.read(iprot); - isset_unit = true; - } else { - xfer += iprot->skip(ftype); - } - break; - default: - xfer += iprot->skip(ftype); - break; - } - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - if (!isset_isAdjustedToUTC) - throw TProtocolException(TProtocolException::INVALID_DATA); - if (!isset_unit) - throw TProtocolException(TProtocolException::INVALID_DATA); - return xfer; -} - -uint32_t TimestampType::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("TimestampType"); - - xfer += oprot->writeFieldBegin("isAdjustedToUTC", ::apache::thrift::protocol::T_BOOL, 1); - xfer += oprot->writeBool(this->isAdjustedToUTC); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldBegin("unit", ::apache::thrift::protocol::T_STRUCT, 2); - xfer += this->unit.write(oprot); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(TimestampType &a, TimestampType &b) { - using ::std::swap; - swap(a.isAdjustedToUTC, b.isAdjustedToUTC); - swap(a.unit, b.unit); -} - -TimestampType::TimestampType(const TimestampType& other26) { - isAdjustedToUTC = other26.isAdjustedToUTC; - unit = other26.unit; -} -TimestampType& TimestampType::operator=(const TimestampType& other27) { - isAdjustedToUTC = other27.isAdjustedToUTC; - unit = other27.unit; - return *this; -} -void TimestampType::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "TimestampType("; - out << "isAdjustedToUTC=" << to_string(isAdjustedToUTC); - out << ", " << "unit=" << to_string(unit); - out << ")"; -} - - -TimeType::~TimeType() throw() { -} - - -void TimeType::__set_isAdjustedToUTC(const bool val) { - this->isAdjustedToUTC = val; -} - -void TimeType::__set_unit(const TimeUnit& val) { - this->unit = val; -} -std::ostream& operator<<(std::ostream& out, const TimeType& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t TimeType::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - bool isset_isAdjustedToUTC = false; - bool isset_unit = false; - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - switch (fid) - { - case 1: - if (ftype == ::apache::thrift::protocol::T_BOOL) { - xfer += iprot->readBool(this->isAdjustedToUTC); - isset_isAdjustedToUTC = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 2: - if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->unit.read(iprot); - isset_unit = true; - } else { - xfer += iprot->skip(ftype); - } - break; - default: - xfer += iprot->skip(ftype); - break; - } - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - if (!isset_isAdjustedToUTC) - throw TProtocolException(TProtocolException::INVALID_DATA); - if (!isset_unit) - throw TProtocolException(TProtocolException::INVALID_DATA); - return xfer; -} - -uint32_t TimeType::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("TimeType"); - - xfer += oprot->writeFieldBegin("isAdjustedToUTC", ::apache::thrift::protocol::T_BOOL, 1); - xfer += oprot->writeBool(this->isAdjustedToUTC); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldBegin("unit", ::apache::thrift::protocol::T_STRUCT, 2); - xfer += this->unit.write(oprot); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(TimeType &a, TimeType &b) { - using ::std::swap; - swap(a.isAdjustedToUTC, b.isAdjustedToUTC); - swap(a.unit, b.unit); -} - -TimeType::TimeType(const TimeType& other28) { - isAdjustedToUTC = other28.isAdjustedToUTC; - unit = other28.unit; -} -TimeType& TimeType::operator=(const TimeType& other29) { - isAdjustedToUTC = other29.isAdjustedToUTC; - unit = other29.unit; - return *this; -} -void TimeType::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "TimeType("; - out << "isAdjustedToUTC=" << to_string(isAdjustedToUTC); - out << ", " << "unit=" << to_string(unit); - out << ")"; -} - - -IntType::~IntType() throw() { -} - - -void IntType::__set_bitWidth(const int8_t val) { - this->bitWidth = val; -} - -void IntType::__set_isSigned(const bool val) { - this->isSigned = val; -} -std::ostream& operator<<(std::ostream& out, const IntType& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t IntType::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - bool isset_bitWidth = false; - bool isset_isSigned = false; - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - switch (fid) - { - case 1: - if (ftype == ::apache::thrift::protocol::T_BYTE) { - xfer += iprot->readByte(this->bitWidth); - isset_bitWidth = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 2: - if (ftype == ::apache::thrift::protocol::T_BOOL) { - xfer += iprot->readBool(this->isSigned); - isset_isSigned = true; - } else { - xfer += iprot->skip(ftype); - } - break; - default: - xfer += iprot->skip(ftype); - break; - } - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - if (!isset_bitWidth) - throw TProtocolException(TProtocolException::INVALID_DATA); - if (!isset_isSigned) - throw TProtocolException(TProtocolException::INVALID_DATA); - return xfer; -} - -uint32_t IntType::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("IntType"); - - xfer += oprot->writeFieldBegin("bitWidth", ::apache::thrift::protocol::T_BYTE, 1); - xfer += oprot->writeByte(this->bitWidth); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldBegin("isSigned", ::apache::thrift::protocol::T_BOOL, 2); - xfer += oprot->writeBool(this->isSigned); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(IntType &a, IntType &b) { - using ::std::swap; - swap(a.bitWidth, b.bitWidth); - swap(a.isSigned, b.isSigned); -} - -IntType::IntType(const IntType& other30) { - bitWidth = other30.bitWidth; - isSigned = other30.isSigned; -} -IntType& IntType::operator=(const IntType& other31) { - bitWidth = other31.bitWidth; - isSigned = other31.isSigned; - return *this; -} -void IntType::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "IntType("; - out << "bitWidth=" << to_string(bitWidth); - out << ", " << "isSigned=" << to_string(isSigned); - out << ")"; -} - - -JsonType::~JsonType() throw() { -} - -std::ostream& operator<<(std::ostream& out, const JsonType& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t JsonType::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - xfer += iprot->skip(ftype); - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - return xfer; -} - -uint32_t JsonType::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("JsonType"); - - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(JsonType &a, JsonType &b) { - using ::std::swap; - (void) a; - (void) b; -} - -JsonType::JsonType(const JsonType& other32) { - (void) other32; -} -JsonType& JsonType::operator=(const JsonType& other33) { - (void) other33; - return *this; -} -void JsonType::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "JsonType("; - out << ")"; -} - - -BsonType::~BsonType() throw() { -} - -std::ostream& operator<<(std::ostream& out, const BsonType& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t BsonType::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - xfer += iprot->skip(ftype); - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - return xfer; -} - -uint32_t BsonType::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("BsonType"); - - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(BsonType &a, BsonType &b) { - using ::std::swap; - (void) a; - (void) b; -} - -BsonType::BsonType(const BsonType& other34) { - (void) other34; -} -BsonType& BsonType::operator=(const BsonType& other35) { - (void) other35; - return *this; -} -void BsonType::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "BsonType("; - out << ")"; -} - - -LogicalType::~LogicalType() throw() { -} - - -void LogicalType::__set_STRING(const StringType& val) { - this->STRING = val; -__isset.STRING = true; -} - -void LogicalType::__set_MAP(const MapType& val) { - this->MAP = val; -__isset.MAP = true; -} - -void LogicalType::__set_LIST(const ListType& val) { - this->LIST = val; -__isset.LIST = true; -} - -void LogicalType::__set_ENUM(const EnumType& val) { - this->ENUM = val; -__isset.ENUM = true; -} - -void LogicalType::__set_DECIMAL(const DecimalType& val) { - this->DECIMAL = val; -__isset.DECIMAL = true; -} - -void LogicalType::__set_DATE(const DateType& val) { - this->DATE = val; -__isset.DATE = true; -} - -void LogicalType::__set_TIME(const TimeType& val) { - this->TIME = val; -__isset.TIME = true; -} - -void LogicalType::__set_TIMESTAMP(const TimestampType& val) { - this->TIMESTAMP = val; -__isset.TIMESTAMP = true; -} - -void LogicalType::__set_INTEGER(const IntType& val) { - this->INTEGER = val; -__isset.INTEGER = true; -} - -void LogicalType::__set_UNKNOWN(const NullType& val) { - this->UNKNOWN = val; -__isset.UNKNOWN = true; -} - -void LogicalType::__set_JSON(const JsonType& val) { - this->JSON = val; -__isset.JSON = true; -} - -void LogicalType::__set_BSON(const BsonType& val) { - this->BSON = val; -__isset.BSON = true; -} - -void LogicalType::__set_UUID(const UUIDType& val) { - this->UUID = val; -__isset.UUID = true; -} -std::ostream& operator<<(std::ostream& out, const LogicalType& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t LogicalType::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - switch (fid) - { - case 1: - if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->STRING.read(iprot); - this->__isset.STRING = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 2: - if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->MAP.read(iprot); - this->__isset.MAP = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 3: - if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->LIST.read(iprot); - this->__isset.LIST = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 4: - if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->ENUM.read(iprot); - this->__isset.ENUM = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 5: - if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->DECIMAL.read(iprot); - this->__isset.DECIMAL = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 6: - if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->DATE.read(iprot); - this->__isset.DATE = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 7: - if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->TIME.read(iprot); - this->__isset.TIME = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 8: - if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->TIMESTAMP.read(iprot); - this->__isset.TIMESTAMP = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 10: - if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->INTEGER.read(iprot); - this->__isset.INTEGER = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 11: - if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->UNKNOWN.read(iprot); - this->__isset.UNKNOWN = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 12: - if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->JSON.read(iprot); - this->__isset.JSON = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 13: - if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->BSON.read(iprot); - this->__isset.BSON = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 14: - if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->UUID.read(iprot); - this->__isset.UUID = true; - } else { - xfer += iprot->skip(ftype); - } - break; - default: - xfer += iprot->skip(ftype); - break; - } - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - return xfer; -} - -uint32_t LogicalType::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("LogicalType"); - - if (this->__isset.STRING) { - xfer += oprot->writeFieldBegin("STRING", ::apache::thrift::protocol::T_STRUCT, 1); - xfer += this->STRING.write(oprot); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.MAP) { - xfer += oprot->writeFieldBegin("MAP", ::apache::thrift::protocol::T_STRUCT, 2); - xfer += this->MAP.write(oprot); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.LIST) { - xfer += oprot->writeFieldBegin("LIST", ::apache::thrift::protocol::T_STRUCT, 3); - xfer += this->LIST.write(oprot); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.ENUM) { - xfer += oprot->writeFieldBegin("ENUM", ::apache::thrift::protocol::T_STRUCT, 4); - xfer += this->ENUM.write(oprot); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.DECIMAL) { - xfer += oprot->writeFieldBegin("DECIMAL", ::apache::thrift::protocol::T_STRUCT, 5); - xfer += this->DECIMAL.write(oprot); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.DATE) { - xfer += oprot->writeFieldBegin("DATE", ::apache::thrift::protocol::T_STRUCT, 6); - xfer += this->DATE.write(oprot); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.TIME) { - xfer += oprot->writeFieldBegin("TIME", ::apache::thrift::protocol::T_STRUCT, 7); - xfer += this->TIME.write(oprot); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.TIMESTAMP) { - xfer += oprot->writeFieldBegin("TIMESTAMP", ::apache::thrift::protocol::T_STRUCT, 8); - xfer += this->TIMESTAMP.write(oprot); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.INTEGER) { - xfer += oprot->writeFieldBegin("INTEGER", ::apache::thrift::protocol::T_STRUCT, 10); - xfer += this->INTEGER.write(oprot); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.UNKNOWN) { - xfer += oprot->writeFieldBegin("UNKNOWN", ::apache::thrift::protocol::T_STRUCT, 11); - xfer += this->UNKNOWN.write(oprot); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.JSON) { - xfer += oprot->writeFieldBegin("JSON", ::apache::thrift::protocol::T_STRUCT, 12); - xfer += this->JSON.write(oprot); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.BSON) { - xfer += oprot->writeFieldBegin("BSON", ::apache::thrift::protocol::T_STRUCT, 13); - xfer += this->BSON.write(oprot); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.UUID) { - xfer += oprot->writeFieldBegin("UUID", ::apache::thrift::protocol::T_STRUCT, 14); - xfer += this->UUID.write(oprot); - xfer += oprot->writeFieldEnd(); - } - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(LogicalType &a, LogicalType &b) { - using ::std::swap; - swap(a.STRING, b.STRING); - swap(a.MAP, b.MAP); - swap(a.LIST, b.LIST); - swap(a.ENUM, b.ENUM); - swap(a.DECIMAL, b.DECIMAL); - swap(a.DATE, b.DATE); - swap(a.TIME, b.TIME); - swap(a.TIMESTAMP, b.TIMESTAMP); - swap(a.INTEGER, b.INTEGER); - swap(a.UNKNOWN, b.UNKNOWN); - swap(a.JSON, b.JSON); - swap(a.BSON, b.BSON); - swap(a.UUID, b.UUID); - swap(a.__isset, b.__isset); -} - -LogicalType::LogicalType(const LogicalType& other36) { - STRING = other36.STRING; - MAP = other36.MAP; - LIST = other36.LIST; - ENUM = other36.ENUM; - DECIMAL = other36.DECIMAL; - DATE = other36.DATE; - TIME = other36.TIME; - TIMESTAMP = other36.TIMESTAMP; - INTEGER = other36.INTEGER; - UNKNOWN = other36.UNKNOWN; - JSON = other36.JSON; - BSON = other36.BSON; - UUID = other36.UUID; - __isset = other36.__isset; -} -LogicalType& LogicalType::operator=(const LogicalType& other37) { - STRING = other37.STRING; - MAP = other37.MAP; - LIST = other37.LIST; - ENUM = other37.ENUM; - DECIMAL = other37.DECIMAL; - DATE = other37.DATE; - TIME = other37.TIME; - TIMESTAMP = other37.TIMESTAMP; - INTEGER = other37.INTEGER; - UNKNOWN = other37.UNKNOWN; - JSON = other37.JSON; - BSON = other37.BSON; - UUID = other37.UUID; - __isset = other37.__isset; - return *this; -} -void LogicalType::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "LogicalType("; - out << "STRING="; (__isset.STRING ? (out << to_string(STRING)) : (out << "")); - out << ", " << "MAP="; (__isset.MAP ? (out << to_string(MAP)) : (out << "")); - out << ", " << "LIST="; (__isset.LIST ? (out << to_string(LIST)) : (out << "")); - out << ", " << "ENUM="; (__isset.ENUM ? (out << to_string(ENUM)) : (out << "")); - out << ", " << "DECIMAL="; (__isset.DECIMAL ? (out << to_string(DECIMAL)) : (out << "")); - out << ", " << "DATE="; (__isset.DATE ? (out << to_string(DATE)) : (out << "")); - out << ", " << "TIME="; (__isset.TIME ? (out << to_string(TIME)) : (out << "")); - out << ", " << "TIMESTAMP="; (__isset.TIMESTAMP ? (out << to_string(TIMESTAMP)) : (out << "")); - out << ", " << "INTEGER="; (__isset.INTEGER ? (out << to_string(INTEGER)) : (out << "")); - out << ", " << "UNKNOWN="; (__isset.UNKNOWN ? (out << to_string(UNKNOWN)) : (out << "")); - out << ", " << "JSON="; (__isset.JSON ? (out << to_string(JSON)) : (out << "")); - out << ", " << "BSON="; (__isset.BSON ? (out << to_string(BSON)) : (out << "")); - out << ", " << "UUID="; (__isset.UUID ? (out << to_string(UUID)) : (out << "")); - out << ")"; -} - - -SchemaElement::~SchemaElement() throw() { -} - - -void SchemaElement::__set_type(const Type::type val) { - this->type = val; -__isset.type = true; -} - -void SchemaElement::__set_type_length(const int32_t val) { - this->type_length = val; -__isset.type_length = true; -} - -void SchemaElement::__set_repetition_type(const FieldRepetitionType::type val) { - this->repetition_type = val; -__isset.repetition_type = true; -} - -void SchemaElement::__set_name(const std::string& val) { - this->name = val; -} - -void SchemaElement::__set_num_children(const int32_t val) { - this->num_children = val; -__isset.num_children = true; -} - -void SchemaElement::__set_converted_type(const ConvertedType::type val) { - this->converted_type = val; -__isset.converted_type = true; -} - -void SchemaElement::__set_scale(const int32_t val) { - this->scale = val; -__isset.scale = true; -} - -void SchemaElement::__set_precision(const int32_t val) { - this->precision = val; -__isset.precision = true; -} - -void SchemaElement::__set_field_id(const int32_t val) { - this->field_id = val; -__isset.field_id = true; -} - -void SchemaElement::__set_logicalType(const LogicalType& val) { - this->logicalType = val; -__isset.logicalType = true; -} -std::ostream& operator<<(std::ostream& out, const SchemaElement& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t SchemaElement::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - bool isset_name = false; - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - switch (fid) - { - case 1: - if (ftype == ::apache::thrift::protocol::T_I32) { - int32_t ecast38; - xfer += iprot->readI32(ecast38); - this->type = (Type::type)ecast38; - this->__isset.type = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 2: - if (ftype == ::apache::thrift::protocol::T_I32) { - xfer += iprot->readI32(this->type_length); - this->__isset.type_length = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 3: - if (ftype == ::apache::thrift::protocol::T_I32) { - int32_t ecast39; - xfer += iprot->readI32(ecast39); - this->repetition_type = (FieldRepetitionType::type)ecast39; - this->__isset.repetition_type = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 4: - if (ftype == ::apache::thrift::protocol::T_STRING) { - xfer += iprot->readString(this->name); - isset_name = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 5: - if (ftype == ::apache::thrift::protocol::T_I32) { - xfer += iprot->readI32(this->num_children); - this->__isset.num_children = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 6: - if (ftype == ::apache::thrift::protocol::T_I32) { - int32_t ecast40; - xfer += iprot->readI32(ecast40); - this->converted_type = (ConvertedType::type)ecast40; - this->__isset.converted_type = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 7: - if (ftype == ::apache::thrift::protocol::T_I32) { - xfer += iprot->readI32(this->scale); - this->__isset.scale = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 8: - if (ftype == ::apache::thrift::protocol::T_I32) { - xfer += iprot->readI32(this->precision); - this->__isset.precision = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 9: - if (ftype == ::apache::thrift::protocol::T_I32) { - xfer += iprot->readI32(this->field_id); - this->__isset.field_id = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 10: - if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->logicalType.read(iprot); - this->__isset.logicalType = true; - } else { - xfer += iprot->skip(ftype); - } - break; - default: - xfer += iprot->skip(ftype); - break; - } - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - if (!isset_name) - throw TProtocolException(TProtocolException::INVALID_DATA); - return xfer; -} - -uint32_t SchemaElement::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("SchemaElement"); - - if (this->__isset.type) { - xfer += oprot->writeFieldBegin("type", ::apache::thrift::protocol::T_I32, 1); - xfer += oprot->writeI32((int32_t)this->type); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.type_length) { - xfer += oprot->writeFieldBegin("type_length", ::apache::thrift::protocol::T_I32, 2); - xfer += oprot->writeI32(this->type_length); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.repetition_type) { - xfer += oprot->writeFieldBegin("repetition_type", ::apache::thrift::protocol::T_I32, 3); - xfer += oprot->writeI32((int32_t)this->repetition_type); - xfer += oprot->writeFieldEnd(); - } - xfer += oprot->writeFieldBegin("name", ::apache::thrift::protocol::T_STRING, 4); - xfer += oprot->writeString(this->name); - xfer += oprot->writeFieldEnd(); - - if (this->__isset.num_children) { - xfer += oprot->writeFieldBegin("num_children", ::apache::thrift::protocol::T_I32, 5); - xfer += oprot->writeI32(this->num_children); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.converted_type) { - xfer += oprot->writeFieldBegin("converted_type", ::apache::thrift::protocol::T_I32, 6); - xfer += oprot->writeI32((int32_t)this->converted_type); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.scale) { - xfer += oprot->writeFieldBegin("scale", ::apache::thrift::protocol::T_I32, 7); - xfer += oprot->writeI32(this->scale); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.precision) { - xfer += oprot->writeFieldBegin("precision", ::apache::thrift::protocol::T_I32, 8); - xfer += oprot->writeI32(this->precision); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.field_id) { - xfer += oprot->writeFieldBegin("field_id", ::apache::thrift::protocol::T_I32, 9); - xfer += oprot->writeI32(this->field_id); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.logicalType) { - xfer += oprot->writeFieldBegin("logicalType", ::apache::thrift::protocol::T_STRUCT, 10); - xfer += this->logicalType.write(oprot); - xfer += oprot->writeFieldEnd(); - } - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(SchemaElement &a, SchemaElement &b) { - using ::std::swap; - swap(a.type, b.type); - swap(a.type_length, b.type_length); - swap(a.repetition_type, b.repetition_type); - swap(a.name, b.name); - swap(a.num_children, b.num_children); - swap(a.converted_type, b.converted_type); - swap(a.scale, b.scale); - swap(a.precision, b.precision); - swap(a.field_id, b.field_id); - swap(a.logicalType, b.logicalType); - swap(a.__isset, b.__isset); -} - -SchemaElement::SchemaElement(const SchemaElement& other41) { - type = other41.type; - type_length = other41.type_length; - repetition_type = other41.repetition_type; - name = other41.name; - num_children = other41.num_children; - converted_type = other41.converted_type; - scale = other41.scale; - precision = other41.precision; - field_id = other41.field_id; - logicalType = other41.logicalType; - __isset = other41.__isset; -} -SchemaElement& SchemaElement::operator=(const SchemaElement& other42) { - type = other42.type; - type_length = other42.type_length; - repetition_type = other42.repetition_type; - name = other42.name; - num_children = other42.num_children; - converted_type = other42.converted_type; - scale = other42.scale; - precision = other42.precision; - field_id = other42.field_id; - logicalType = other42.logicalType; - __isset = other42.__isset; - return *this; -} -void SchemaElement::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "SchemaElement("; - out << "type="; (__isset.type ? (out << to_string(type)) : (out << "")); - out << ", " << "type_length="; (__isset.type_length ? (out << to_string(type_length)) : (out << "")); - out << ", " << "repetition_type="; (__isset.repetition_type ? (out << to_string(repetition_type)) : (out << "")); - out << ", " << "name=" << to_string(name); - out << ", " << "num_children="; (__isset.num_children ? (out << to_string(num_children)) : (out << "")); - out << ", " << "converted_type="; (__isset.converted_type ? (out << to_string(converted_type)) : (out << "")); - out << ", " << "scale="; (__isset.scale ? (out << to_string(scale)) : (out << "")); - out << ", " << "precision="; (__isset.precision ? (out << to_string(precision)) : (out << "")); - out << ", " << "field_id="; (__isset.field_id ? (out << to_string(field_id)) : (out << "")); - out << ", " << "logicalType="; (__isset.logicalType ? (out << to_string(logicalType)) : (out << "")); - out << ")"; -} - - -DataPageHeader::~DataPageHeader() throw() { -} - - -void DataPageHeader::__set_num_values(const int32_t val) { - this->num_values = val; -} - -void DataPageHeader::__set_encoding(const Encoding::type val) { - this->encoding = val; -} - -void DataPageHeader::__set_definition_level_encoding(const Encoding::type val) { - this->definition_level_encoding = val; -} - -void DataPageHeader::__set_repetition_level_encoding(const Encoding::type val) { - this->repetition_level_encoding = val; -} - -void DataPageHeader::__set_statistics(const Statistics& val) { - this->statistics = val; -__isset.statistics = true; -} -std::ostream& operator<<(std::ostream& out, const DataPageHeader& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t DataPageHeader::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - bool isset_num_values = false; - bool isset_encoding = false; - bool isset_definition_level_encoding = false; - bool isset_repetition_level_encoding = false; - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - switch (fid) - { - case 1: - if (ftype == ::apache::thrift::protocol::T_I32) { - xfer += iprot->readI32(this->num_values); - isset_num_values = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 2: - if (ftype == ::apache::thrift::protocol::T_I32) { - int32_t ecast43; - xfer += iprot->readI32(ecast43); - this->encoding = (Encoding::type)ecast43; - isset_encoding = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 3: - if (ftype == ::apache::thrift::protocol::T_I32) { - int32_t ecast44; - xfer += iprot->readI32(ecast44); - this->definition_level_encoding = (Encoding::type)ecast44; - isset_definition_level_encoding = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 4: - if (ftype == ::apache::thrift::protocol::T_I32) { - int32_t ecast45; - xfer += iprot->readI32(ecast45); - this->repetition_level_encoding = (Encoding::type)ecast45; - isset_repetition_level_encoding = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 5: - if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->statistics.read(iprot); - this->__isset.statistics = true; - } else { - xfer += iprot->skip(ftype); - } - break; - default: - xfer += iprot->skip(ftype); - break; - } - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - if (!isset_num_values) - throw TProtocolException(TProtocolException::INVALID_DATA); - if (!isset_encoding) - throw TProtocolException(TProtocolException::INVALID_DATA); - if (!isset_definition_level_encoding) - throw TProtocolException(TProtocolException::INVALID_DATA); - if (!isset_repetition_level_encoding) - throw TProtocolException(TProtocolException::INVALID_DATA); - return xfer; -} - -uint32_t DataPageHeader::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("DataPageHeader"); - - xfer += oprot->writeFieldBegin("num_values", ::apache::thrift::protocol::T_I32, 1); - xfer += oprot->writeI32(this->num_values); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldBegin("encoding", ::apache::thrift::protocol::T_I32, 2); - xfer += oprot->writeI32((int32_t)this->encoding); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldBegin("definition_level_encoding", ::apache::thrift::protocol::T_I32, 3); - xfer += oprot->writeI32((int32_t)this->definition_level_encoding); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldBegin("repetition_level_encoding", ::apache::thrift::protocol::T_I32, 4); - xfer += oprot->writeI32((int32_t)this->repetition_level_encoding); - xfer += oprot->writeFieldEnd(); - - if (this->__isset.statistics) { - xfer += oprot->writeFieldBegin("statistics", ::apache::thrift::protocol::T_STRUCT, 5); - xfer += this->statistics.write(oprot); - xfer += oprot->writeFieldEnd(); - } - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(DataPageHeader &a, DataPageHeader &b) { - using ::std::swap; - swap(a.num_values, b.num_values); - swap(a.encoding, b.encoding); - swap(a.definition_level_encoding, b.definition_level_encoding); - swap(a.repetition_level_encoding, b.repetition_level_encoding); - swap(a.statistics, b.statistics); - swap(a.__isset, b.__isset); -} - -DataPageHeader::DataPageHeader(const DataPageHeader& other46) { - num_values = other46.num_values; - encoding = other46.encoding; - definition_level_encoding = other46.definition_level_encoding; - repetition_level_encoding = other46.repetition_level_encoding; - statistics = other46.statistics; - __isset = other46.__isset; -} -DataPageHeader& DataPageHeader::operator=(const DataPageHeader& other47) { - num_values = other47.num_values; - encoding = other47.encoding; - definition_level_encoding = other47.definition_level_encoding; - repetition_level_encoding = other47.repetition_level_encoding; - statistics = other47.statistics; - __isset = other47.__isset; - return *this; -} -void DataPageHeader::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "DataPageHeader("; - out << "num_values=" << to_string(num_values); - out << ", " << "encoding=" << to_string(encoding); - out << ", " << "definition_level_encoding=" << to_string(definition_level_encoding); - out << ", " << "repetition_level_encoding=" << to_string(repetition_level_encoding); - out << ", " << "statistics="; (__isset.statistics ? (out << to_string(statistics)) : (out << "")); - out << ")"; -} - - -IndexPageHeader::~IndexPageHeader() throw() { -} - -std::ostream& operator<<(std::ostream& out, const IndexPageHeader& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t IndexPageHeader::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - xfer += iprot->skip(ftype); - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - return xfer; -} - -uint32_t IndexPageHeader::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("IndexPageHeader"); - - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(IndexPageHeader &a, IndexPageHeader &b) { - using ::std::swap; - (void) a; - (void) b; -} - -IndexPageHeader::IndexPageHeader(const IndexPageHeader& other48) { - (void) other48; -} -IndexPageHeader& IndexPageHeader::operator=(const IndexPageHeader& other49) { - (void) other49; - return *this; -} -void IndexPageHeader::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "IndexPageHeader("; - out << ")"; -} - - -DictionaryPageHeader::~DictionaryPageHeader() throw() { -} - - -void DictionaryPageHeader::__set_num_values(const int32_t val) { - this->num_values = val; -} - -void DictionaryPageHeader::__set_encoding(const Encoding::type val) { - this->encoding = val; -} - -void DictionaryPageHeader::__set_is_sorted(const bool val) { - this->is_sorted = val; -__isset.is_sorted = true; -} -std::ostream& operator<<(std::ostream& out, const DictionaryPageHeader& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t DictionaryPageHeader::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - bool isset_num_values = false; - bool isset_encoding = false; - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - switch (fid) - { - case 1: - if (ftype == ::apache::thrift::protocol::T_I32) { - xfer += iprot->readI32(this->num_values); - isset_num_values = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 2: - if (ftype == ::apache::thrift::protocol::T_I32) { - int32_t ecast50; - xfer += iprot->readI32(ecast50); - this->encoding = (Encoding::type)ecast50; - isset_encoding = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 3: - if (ftype == ::apache::thrift::protocol::T_BOOL) { - xfer += iprot->readBool(this->is_sorted); - this->__isset.is_sorted = true; - } else { - xfer += iprot->skip(ftype); - } - break; - default: - xfer += iprot->skip(ftype); - break; - } - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - if (!isset_num_values) - throw TProtocolException(TProtocolException::INVALID_DATA); - if (!isset_encoding) - throw TProtocolException(TProtocolException::INVALID_DATA); - return xfer; -} - -uint32_t DictionaryPageHeader::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("DictionaryPageHeader"); - - xfer += oprot->writeFieldBegin("num_values", ::apache::thrift::protocol::T_I32, 1); - xfer += oprot->writeI32(this->num_values); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldBegin("encoding", ::apache::thrift::protocol::T_I32, 2); - xfer += oprot->writeI32((int32_t)this->encoding); - xfer += oprot->writeFieldEnd(); - - if (this->__isset.is_sorted) { - xfer += oprot->writeFieldBegin("is_sorted", ::apache::thrift::protocol::T_BOOL, 3); - xfer += oprot->writeBool(this->is_sorted); - xfer += oprot->writeFieldEnd(); - } - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(DictionaryPageHeader &a, DictionaryPageHeader &b) { - using ::std::swap; - swap(a.num_values, b.num_values); - swap(a.encoding, b.encoding); - swap(a.is_sorted, b.is_sorted); - swap(a.__isset, b.__isset); -} - -DictionaryPageHeader::DictionaryPageHeader(const DictionaryPageHeader& other51) { - num_values = other51.num_values; - encoding = other51.encoding; - is_sorted = other51.is_sorted; - __isset = other51.__isset; -} -DictionaryPageHeader& DictionaryPageHeader::operator=(const DictionaryPageHeader& other52) { - num_values = other52.num_values; - encoding = other52.encoding; - is_sorted = other52.is_sorted; - __isset = other52.__isset; - return *this; -} -void DictionaryPageHeader::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "DictionaryPageHeader("; - out << "num_values=" << to_string(num_values); - out << ", " << "encoding=" << to_string(encoding); - out << ", " << "is_sorted="; (__isset.is_sorted ? (out << to_string(is_sorted)) : (out << "")); - out << ")"; -} - - -DataPageHeaderV2::~DataPageHeaderV2() throw() { -} - - -void DataPageHeaderV2::__set_num_values(const int32_t val) { - this->num_values = val; -} - -void DataPageHeaderV2::__set_num_nulls(const int32_t val) { - this->num_nulls = val; -} - -void DataPageHeaderV2::__set_num_rows(const int32_t val) { - this->num_rows = val; -} - -void DataPageHeaderV2::__set_encoding(const Encoding::type val) { - this->encoding = val; -} - -void DataPageHeaderV2::__set_definition_levels_byte_length(const int32_t val) { - this->definition_levels_byte_length = val; -} - -void DataPageHeaderV2::__set_repetition_levels_byte_length(const int32_t val) { - this->repetition_levels_byte_length = val; -} - -void DataPageHeaderV2::__set_is_compressed(const bool val) { - this->is_compressed = val; -__isset.is_compressed = true; -} - -void DataPageHeaderV2::__set_statistics(const Statistics& val) { - this->statistics = val; -__isset.statistics = true; -} -std::ostream& operator<<(std::ostream& out, const DataPageHeaderV2& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t DataPageHeaderV2::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - bool isset_num_values = false; - bool isset_num_nulls = false; - bool isset_num_rows = false; - bool isset_encoding = false; - bool isset_definition_levels_byte_length = false; - bool isset_repetition_levels_byte_length = false; - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - switch (fid) - { - case 1: - if (ftype == ::apache::thrift::protocol::T_I32) { - xfer += iprot->readI32(this->num_values); - isset_num_values = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 2: - if (ftype == ::apache::thrift::protocol::T_I32) { - xfer += iprot->readI32(this->num_nulls); - isset_num_nulls = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 3: - if (ftype == ::apache::thrift::protocol::T_I32) { - xfer += iprot->readI32(this->num_rows); - isset_num_rows = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 4: - if (ftype == ::apache::thrift::protocol::T_I32) { - int32_t ecast53; - xfer += iprot->readI32(ecast53); - this->encoding = (Encoding::type)ecast53; - isset_encoding = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 5: - if (ftype == ::apache::thrift::protocol::T_I32) { - xfer += iprot->readI32(this->definition_levels_byte_length); - isset_definition_levels_byte_length = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 6: - if (ftype == ::apache::thrift::protocol::T_I32) { - xfer += iprot->readI32(this->repetition_levels_byte_length); - isset_repetition_levels_byte_length = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 7: - if (ftype == ::apache::thrift::protocol::T_BOOL) { - xfer += iprot->readBool(this->is_compressed); - this->__isset.is_compressed = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 8: - if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->statistics.read(iprot); - this->__isset.statistics = true; - } else { - xfer += iprot->skip(ftype); - } - break; - default: - xfer += iprot->skip(ftype); - break; - } - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - if (!isset_num_values) - throw TProtocolException(TProtocolException::INVALID_DATA); - if (!isset_num_nulls) - throw TProtocolException(TProtocolException::INVALID_DATA); - if (!isset_num_rows) - throw TProtocolException(TProtocolException::INVALID_DATA); - if (!isset_encoding) - throw TProtocolException(TProtocolException::INVALID_DATA); - if (!isset_definition_levels_byte_length) - throw TProtocolException(TProtocolException::INVALID_DATA); - if (!isset_repetition_levels_byte_length) - throw TProtocolException(TProtocolException::INVALID_DATA); - return xfer; -} - -uint32_t DataPageHeaderV2::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("DataPageHeaderV2"); - - xfer += oprot->writeFieldBegin("num_values", ::apache::thrift::protocol::T_I32, 1); - xfer += oprot->writeI32(this->num_values); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldBegin("num_nulls", ::apache::thrift::protocol::T_I32, 2); - xfer += oprot->writeI32(this->num_nulls); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldBegin("num_rows", ::apache::thrift::protocol::T_I32, 3); - xfer += oprot->writeI32(this->num_rows); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldBegin("encoding", ::apache::thrift::protocol::T_I32, 4); - xfer += oprot->writeI32((int32_t)this->encoding); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldBegin("definition_levels_byte_length", ::apache::thrift::protocol::T_I32, 5); - xfer += oprot->writeI32(this->definition_levels_byte_length); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldBegin("repetition_levels_byte_length", ::apache::thrift::protocol::T_I32, 6); - xfer += oprot->writeI32(this->repetition_levels_byte_length); - xfer += oprot->writeFieldEnd(); - - if (this->__isset.is_compressed) { - xfer += oprot->writeFieldBegin("is_compressed", ::apache::thrift::protocol::T_BOOL, 7); - xfer += oprot->writeBool(this->is_compressed); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.statistics) { - xfer += oprot->writeFieldBegin("statistics", ::apache::thrift::protocol::T_STRUCT, 8); - xfer += this->statistics.write(oprot); - xfer += oprot->writeFieldEnd(); - } - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(DataPageHeaderV2 &a, DataPageHeaderV2 &b) { - using ::std::swap; - swap(a.num_values, b.num_values); - swap(a.num_nulls, b.num_nulls); - swap(a.num_rows, b.num_rows); - swap(a.encoding, b.encoding); - swap(a.definition_levels_byte_length, b.definition_levels_byte_length); - swap(a.repetition_levels_byte_length, b.repetition_levels_byte_length); - swap(a.is_compressed, b.is_compressed); - swap(a.statistics, b.statistics); - swap(a.__isset, b.__isset); -} - -DataPageHeaderV2::DataPageHeaderV2(const DataPageHeaderV2& other54) { - num_values = other54.num_values; - num_nulls = other54.num_nulls; - num_rows = other54.num_rows; - encoding = other54.encoding; - definition_levels_byte_length = other54.definition_levels_byte_length; - repetition_levels_byte_length = other54.repetition_levels_byte_length; - is_compressed = other54.is_compressed; - statistics = other54.statistics; - __isset = other54.__isset; -} -DataPageHeaderV2& DataPageHeaderV2::operator=(const DataPageHeaderV2& other55) { - num_values = other55.num_values; - num_nulls = other55.num_nulls; - num_rows = other55.num_rows; - encoding = other55.encoding; - definition_levels_byte_length = other55.definition_levels_byte_length; - repetition_levels_byte_length = other55.repetition_levels_byte_length; - is_compressed = other55.is_compressed; - statistics = other55.statistics; - __isset = other55.__isset; - return *this; -} -void DataPageHeaderV2::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "DataPageHeaderV2("; - out << "num_values=" << to_string(num_values); - out << ", " << "num_nulls=" << to_string(num_nulls); - out << ", " << "num_rows=" << to_string(num_rows); - out << ", " << "encoding=" << to_string(encoding); - out << ", " << "definition_levels_byte_length=" << to_string(definition_levels_byte_length); - out << ", " << "repetition_levels_byte_length=" << to_string(repetition_levels_byte_length); - out << ", " << "is_compressed="; (__isset.is_compressed ? (out << to_string(is_compressed)) : (out << "")); - out << ", " << "statistics="; (__isset.statistics ? (out << to_string(statistics)) : (out << "")); - out << ")"; -} - - -SplitBlockAlgorithm::~SplitBlockAlgorithm() throw() { -} - -std::ostream& operator<<(std::ostream& out, const SplitBlockAlgorithm& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t SplitBlockAlgorithm::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - xfer += iprot->skip(ftype); - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - return xfer; -} - -uint32_t SplitBlockAlgorithm::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("SplitBlockAlgorithm"); - - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(SplitBlockAlgorithm &a, SplitBlockAlgorithm &b) { - using ::std::swap; - (void) a; - (void) b; -} - -SplitBlockAlgorithm::SplitBlockAlgorithm(const SplitBlockAlgorithm& other56) { - (void) other56; -} -SplitBlockAlgorithm& SplitBlockAlgorithm::operator=(const SplitBlockAlgorithm& other57) { - (void) other57; - return *this; -} -void SplitBlockAlgorithm::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "SplitBlockAlgorithm("; - out << ")"; -} - - -BloomFilterAlgorithm::~BloomFilterAlgorithm() throw() { -} - - -void BloomFilterAlgorithm::__set_BLOCK(const SplitBlockAlgorithm& val) { - this->BLOCK = val; -__isset.BLOCK = true; -} -std::ostream& operator<<(std::ostream& out, const BloomFilterAlgorithm& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t BloomFilterAlgorithm::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - switch (fid) - { - case 1: - if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->BLOCK.read(iprot); - this->__isset.BLOCK = true; - } else { - xfer += iprot->skip(ftype); - } - break; - default: - xfer += iprot->skip(ftype); - break; - } - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - return xfer; -} - -uint32_t BloomFilterAlgorithm::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("BloomFilterAlgorithm"); - - if (this->__isset.BLOCK) { - xfer += oprot->writeFieldBegin("BLOCK", ::apache::thrift::protocol::T_STRUCT, 1); - xfer += this->BLOCK.write(oprot); - xfer += oprot->writeFieldEnd(); - } - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(BloomFilterAlgorithm &a, BloomFilterAlgorithm &b) { - using ::std::swap; - swap(a.BLOCK, b.BLOCK); - swap(a.__isset, b.__isset); -} - -BloomFilterAlgorithm::BloomFilterAlgorithm(const BloomFilterAlgorithm& other58) { - BLOCK = other58.BLOCK; - __isset = other58.__isset; -} -BloomFilterAlgorithm& BloomFilterAlgorithm::operator=(const BloomFilterAlgorithm& other59) { - BLOCK = other59.BLOCK; - __isset = other59.__isset; - return *this; -} -void BloomFilterAlgorithm::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "BloomFilterAlgorithm("; - out << "BLOCK="; (__isset.BLOCK ? (out << to_string(BLOCK)) : (out << "")); - out << ")"; -} - - -XxHash::~XxHash() throw() { -} - -std::ostream& operator<<(std::ostream& out, const XxHash& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t XxHash::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - xfer += iprot->skip(ftype); - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - return xfer; -} - -uint32_t XxHash::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("XxHash"); - - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(XxHash &a, XxHash &b) { - using ::std::swap; - (void) a; - (void) b; -} - -XxHash::XxHash(const XxHash& other60) { - (void) other60; -} -XxHash& XxHash::operator=(const XxHash& other61) { - (void) other61; - return *this; -} -void XxHash::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "XxHash("; - out << ")"; -} - - -BloomFilterHash::~BloomFilterHash() throw() { -} - - -void BloomFilterHash::__set_XXHASH(const XxHash& val) { - this->XXHASH = val; -__isset.XXHASH = true; -} -std::ostream& operator<<(std::ostream& out, const BloomFilterHash& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t BloomFilterHash::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - switch (fid) - { - case 1: - if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->XXHASH.read(iprot); - this->__isset.XXHASH = true; - } else { - xfer += iprot->skip(ftype); - } - break; - default: - xfer += iprot->skip(ftype); - break; - } - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - return xfer; -} - -uint32_t BloomFilterHash::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("BloomFilterHash"); - - if (this->__isset.XXHASH) { - xfer += oprot->writeFieldBegin("XXHASH", ::apache::thrift::protocol::T_STRUCT, 1); - xfer += this->XXHASH.write(oprot); - xfer += oprot->writeFieldEnd(); - } - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(BloomFilterHash &a, BloomFilterHash &b) { - using ::std::swap; - swap(a.XXHASH, b.XXHASH); - swap(a.__isset, b.__isset); -} - -BloomFilterHash::BloomFilterHash(const BloomFilterHash& other62) { - XXHASH = other62.XXHASH; - __isset = other62.__isset; -} -BloomFilterHash& BloomFilterHash::operator=(const BloomFilterHash& other63) { - XXHASH = other63.XXHASH; - __isset = other63.__isset; - return *this; -} -void BloomFilterHash::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "BloomFilterHash("; - out << "XXHASH="; (__isset.XXHASH ? (out << to_string(XXHASH)) : (out << "")); - out << ")"; -} - - -Uncompressed::~Uncompressed() throw() { -} - -std::ostream& operator<<(std::ostream& out, const Uncompressed& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t Uncompressed::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - xfer += iprot->skip(ftype); - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - return xfer; -} - -uint32_t Uncompressed::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("Uncompressed"); - - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(Uncompressed &a, Uncompressed &b) { - using ::std::swap; - (void) a; - (void) b; -} - -Uncompressed::Uncompressed(const Uncompressed& other64) { - (void) other64; -} -Uncompressed& Uncompressed::operator=(const Uncompressed& other65) { - (void) other65; - return *this; -} -void Uncompressed::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "Uncompressed("; - out << ")"; -} - - -BloomFilterCompression::~BloomFilterCompression() throw() { -} - - -void BloomFilterCompression::__set_UNCOMPRESSED(const Uncompressed& val) { - this->UNCOMPRESSED = val; -__isset.UNCOMPRESSED = true; -} -std::ostream& operator<<(std::ostream& out, const BloomFilterCompression& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t BloomFilterCompression::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - switch (fid) - { - case 1: - if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->UNCOMPRESSED.read(iprot); - this->__isset.UNCOMPRESSED = true; - } else { - xfer += iprot->skip(ftype); - } - break; - default: - xfer += iprot->skip(ftype); - break; - } - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - return xfer; -} - -uint32_t BloomFilterCompression::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("BloomFilterCompression"); - - if (this->__isset.UNCOMPRESSED) { - xfer += oprot->writeFieldBegin("UNCOMPRESSED", ::apache::thrift::protocol::T_STRUCT, 1); - xfer += this->UNCOMPRESSED.write(oprot); - xfer += oprot->writeFieldEnd(); - } - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(BloomFilterCompression &a, BloomFilterCompression &b) { - using ::std::swap; - swap(a.UNCOMPRESSED, b.UNCOMPRESSED); - swap(a.__isset, b.__isset); -} - -BloomFilterCompression::BloomFilterCompression(const BloomFilterCompression& other66) { - UNCOMPRESSED = other66.UNCOMPRESSED; - __isset = other66.__isset; -} -BloomFilterCompression& BloomFilterCompression::operator=(const BloomFilterCompression& other67) { - UNCOMPRESSED = other67.UNCOMPRESSED; - __isset = other67.__isset; - return *this; -} -void BloomFilterCompression::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "BloomFilterCompression("; - out << "UNCOMPRESSED="; (__isset.UNCOMPRESSED ? (out << to_string(UNCOMPRESSED)) : (out << "")); - out << ")"; -} - - -BloomFilterHeader::~BloomFilterHeader() throw() { -} - - -void BloomFilterHeader::__set_numBytes(const int32_t val) { - this->numBytes = val; -} - -void BloomFilterHeader::__set_algorithm(const BloomFilterAlgorithm& val) { - this->algorithm = val; -} - -void BloomFilterHeader::__set_hash(const BloomFilterHash& val) { - this->hash = val; -} - -void BloomFilterHeader::__set_compression(const BloomFilterCompression& val) { - this->compression = val; -} -std::ostream& operator<<(std::ostream& out, const BloomFilterHeader& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t BloomFilterHeader::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - bool isset_numBytes = false; - bool isset_algorithm = false; - bool isset_hash = false; - bool isset_compression = false; - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - switch (fid) - { - case 1: - if (ftype == ::apache::thrift::protocol::T_I32) { - xfer += iprot->readI32(this->numBytes); - isset_numBytes = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 2: - if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->algorithm.read(iprot); - isset_algorithm = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 3: - if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->hash.read(iprot); - isset_hash = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 4: - if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->compression.read(iprot); - isset_compression = true; - } else { - xfer += iprot->skip(ftype); - } - break; - default: - xfer += iprot->skip(ftype); - break; - } - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - if (!isset_numBytes) - throw TProtocolException(TProtocolException::INVALID_DATA); - if (!isset_algorithm) - throw TProtocolException(TProtocolException::INVALID_DATA); - if (!isset_hash) - throw TProtocolException(TProtocolException::INVALID_DATA); - if (!isset_compression) - throw TProtocolException(TProtocolException::INVALID_DATA); - return xfer; -} - -uint32_t BloomFilterHeader::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("BloomFilterHeader"); - - xfer += oprot->writeFieldBegin("numBytes", ::apache::thrift::protocol::T_I32, 1); - xfer += oprot->writeI32(this->numBytes); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldBegin("algorithm", ::apache::thrift::protocol::T_STRUCT, 2); - xfer += this->algorithm.write(oprot); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldBegin("hash", ::apache::thrift::protocol::T_STRUCT, 3); - xfer += this->hash.write(oprot); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldBegin("compression", ::apache::thrift::protocol::T_STRUCT, 4); - xfer += this->compression.write(oprot); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(BloomFilterHeader &a, BloomFilterHeader &b) { - using ::std::swap; - swap(a.numBytes, b.numBytes); - swap(a.algorithm, b.algorithm); - swap(a.hash, b.hash); - swap(a.compression, b.compression); -} - -BloomFilterHeader::BloomFilterHeader(const BloomFilterHeader& other68) { - numBytes = other68.numBytes; - algorithm = other68.algorithm; - hash = other68.hash; - compression = other68.compression; -} -BloomFilterHeader& BloomFilterHeader::operator=(const BloomFilterHeader& other69) { - numBytes = other69.numBytes; - algorithm = other69.algorithm; - hash = other69.hash; - compression = other69.compression; - return *this; -} -void BloomFilterHeader::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "BloomFilterHeader("; - out << "numBytes=" << to_string(numBytes); - out << ", " << "algorithm=" << to_string(algorithm); - out << ", " << "hash=" << to_string(hash); - out << ", " << "compression=" << to_string(compression); - out << ")"; -} - - -PageHeader::~PageHeader() throw() { -} - - -void PageHeader::__set_type(const PageType::type val) { - this->type = val; -} - -void PageHeader::__set_uncompressed_page_size(const int32_t val) { - this->uncompressed_page_size = val; -} - -void PageHeader::__set_compressed_page_size(const int32_t val) { - this->compressed_page_size = val; -} - -void PageHeader::__set_crc(const int32_t val) { - this->crc = val; -__isset.crc = true; -} - -void PageHeader::__set_data_page_header(const DataPageHeader& val) { - this->data_page_header = val; -__isset.data_page_header = true; -} - -void PageHeader::__set_index_page_header(const IndexPageHeader& val) { - this->index_page_header = val; -__isset.index_page_header = true; -} - -void PageHeader::__set_dictionary_page_header(const DictionaryPageHeader& val) { - this->dictionary_page_header = val; -__isset.dictionary_page_header = true; -} - -void PageHeader::__set_data_page_header_v2(const DataPageHeaderV2& val) { - this->data_page_header_v2 = val; -__isset.data_page_header_v2 = true; -} -std::ostream& operator<<(std::ostream& out, const PageHeader& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t PageHeader::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - bool isset_type = false; - bool isset_uncompressed_page_size = false; - bool isset_compressed_page_size = false; - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - switch (fid) - { - case 1: - if (ftype == ::apache::thrift::protocol::T_I32) { - int32_t ecast70; - xfer += iprot->readI32(ecast70); - this->type = (PageType::type)ecast70; - isset_type = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 2: - if (ftype == ::apache::thrift::protocol::T_I32) { - xfer += iprot->readI32(this->uncompressed_page_size); - isset_uncompressed_page_size = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 3: - if (ftype == ::apache::thrift::protocol::T_I32) { - xfer += iprot->readI32(this->compressed_page_size); - isset_compressed_page_size = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 4: - if (ftype == ::apache::thrift::protocol::T_I32) { - xfer += iprot->readI32(this->crc); - this->__isset.crc = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 5: - if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->data_page_header.read(iprot); - this->__isset.data_page_header = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 6: - if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->index_page_header.read(iprot); - this->__isset.index_page_header = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 7: - if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->dictionary_page_header.read(iprot); - this->__isset.dictionary_page_header = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 8: - if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->data_page_header_v2.read(iprot); - this->__isset.data_page_header_v2 = true; - } else { - xfer += iprot->skip(ftype); - } - break; - default: - xfer += iprot->skip(ftype); - break; - } - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - if (!isset_type) - throw TProtocolException(TProtocolException::INVALID_DATA); - if (!isset_uncompressed_page_size) - throw TProtocolException(TProtocolException::INVALID_DATA); - if (!isset_compressed_page_size) - throw TProtocolException(TProtocolException::INVALID_DATA); - return xfer; -} - -uint32_t PageHeader::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("PageHeader"); - - xfer += oprot->writeFieldBegin("type", ::apache::thrift::protocol::T_I32, 1); - xfer += oprot->writeI32((int32_t)this->type); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldBegin("uncompressed_page_size", ::apache::thrift::protocol::T_I32, 2); - xfer += oprot->writeI32(this->uncompressed_page_size); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldBegin("compressed_page_size", ::apache::thrift::protocol::T_I32, 3); - xfer += oprot->writeI32(this->compressed_page_size); - xfer += oprot->writeFieldEnd(); - - if (this->__isset.crc) { - xfer += oprot->writeFieldBegin("crc", ::apache::thrift::protocol::T_I32, 4); - xfer += oprot->writeI32(this->crc); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.data_page_header) { - xfer += oprot->writeFieldBegin("data_page_header", ::apache::thrift::protocol::T_STRUCT, 5); - xfer += this->data_page_header.write(oprot); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.index_page_header) { - xfer += oprot->writeFieldBegin("index_page_header", ::apache::thrift::protocol::T_STRUCT, 6); - xfer += this->index_page_header.write(oprot); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.dictionary_page_header) { - xfer += oprot->writeFieldBegin("dictionary_page_header", ::apache::thrift::protocol::T_STRUCT, 7); - xfer += this->dictionary_page_header.write(oprot); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.data_page_header_v2) { - xfer += oprot->writeFieldBegin("data_page_header_v2", ::apache::thrift::protocol::T_STRUCT, 8); - xfer += this->data_page_header_v2.write(oprot); - xfer += oprot->writeFieldEnd(); - } - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(PageHeader &a, PageHeader &b) { - using ::std::swap; - swap(a.type, b.type); - swap(a.uncompressed_page_size, b.uncompressed_page_size); - swap(a.compressed_page_size, b.compressed_page_size); - swap(a.crc, b.crc); - swap(a.data_page_header, b.data_page_header); - swap(a.index_page_header, b.index_page_header); - swap(a.dictionary_page_header, b.dictionary_page_header); - swap(a.data_page_header_v2, b.data_page_header_v2); - swap(a.__isset, b.__isset); -} - -PageHeader::PageHeader(const PageHeader& other71) { - type = other71.type; - uncompressed_page_size = other71.uncompressed_page_size; - compressed_page_size = other71.compressed_page_size; - crc = other71.crc; - data_page_header = other71.data_page_header; - index_page_header = other71.index_page_header; - dictionary_page_header = other71.dictionary_page_header; - data_page_header_v2 = other71.data_page_header_v2; - __isset = other71.__isset; -} -PageHeader& PageHeader::operator=(const PageHeader& other72) { - type = other72.type; - uncompressed_page_size = other72.uncompressed_page_size; - compressed_page_size = other72.compressed_page_size; - crc = other72.crc; - data_page_header = other72.data_page_header; - index_page_header = other72.index_page_header; - dictionary_page_header = other72.dictionary_page_header; - data_page_header_v2 = other72.data_page_header_v2; - __isset = other72.__isset; - return *this; -} -void PageHeader::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "PageHeader("; - out << "type=" << to_string(type); - out << ", " << "uncompressed_page_size=" << to_string(uncompressed_page_size); - out << ", " << "compressed_page_size=" << to_string(compressed_page_size); - out << ", " << "crc="; (__isset.crc ? (out << to_string(crc)) : (out << "")); - out << ", " << "data_page_header="; (__isset.data_page_header ? (out << to_string(data_page_header)) : (out << "")); - out << ", " << "index_page_header="; (__isset.index_page_header ? (out << to_string(index_page_header)) : (out << "")); - out << ", " << "dictionary_page_header="; (__isset.dictionary_page_header ? (out << to_string(dictionary_page_header)) : (out << "")); - out << ", " << "data_page_header_v2="; (__isset.data_page_header_v2 ? (out << to_string(data_page_header_v2)) : (out << "")); - out << ")"; -} - - -KeyValue::~KeyValue() throw() { -} - - -void KeyValue::__set_key(const std::string& val) { - this->key = val; -} - -void KeyValue::__set_value(const std::string& val) { - this->value = val; -__isset.value = true; -} -std::ostream& operator<<(std::ostream& out, const KeyValue& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t KeyValue::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - bool isset_key = false; - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - switch (fid) - { - case 1: - if (ftype == ::apache::thrift::protocol::T_STRING) { - xfer += iprot->readString(this->key); - isset_key = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 2: - if (ftype == ::apache::thrift::protocol::T_STRING) { - xfer += iprot->readString(this->value); - this->__isset.value = true; - } else { - xfer += iprot->skip(ftype); - } - break; - default: - xfer += iprot->skip(ftype); - break; - } - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - if (!isset_key) - throw TProtocolException(TProtocolException::INVALID_DATA); - return xfer; -} - -uint32_t KeyValue::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("KeyValue"); - - xfer += oprot->writeFieldBegin("key", ::apache::thrift::protocol::T_STRING, 1); - xfer += oprot->writeString(this->key); - xfer += oprot->writeFieldEnd(); - - if (this->__isset.value) { - xfer += oprot->writeFieldBegin("value", ::apache::thrift::protocol::T_STRING, 2); - xfer += oprot->writeString(this->value); - xfer += oprot->writeFieldEnd(); - } - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(KeyValue &a, KeyValue &b) { - using ::std::swap; - swap(a.key, b.key); - swap(a.value, b.value); - swap(a.__isset, b.__isset); -} - -KeyValue::KeyValue(const KeyValue& other73) { - key = other73.key; - value = other73.value; - __isset = other73.__isset; -} -KeyValue& KeyValue::operator=(const KeyValue& other74) { - key = other74.key; - value = other74.value; - __isset = other74.__isset; - return *this; -} -void KeyValue::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "KeyValue("; - out << "key=" << to_string(key); - out << ", " << "value="; (__isset.value ? (out << to_string(value)) : (out << "")); - out << ")"; -} - - -SortingColumn::~SortingColumn() throw() { -} - - -void SortingColumn::__set_column_idx(const int32_t val) { - this->column_idx = val; -} - -void SortingColumn::__set_descending(const bool val) { - this->descending = val; -} - -void SortingColumn::__set_nulls_first(const bool val) { - this->nulls_first = val; -} -std::ostream& operator<<(std::ostream& out, const SortingColumn& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t SortingColumn::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - bool isset_column_idx = false; - bool isset_descending = false; - bool isset_nulls_first = false; - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - switch (fid) - { - case 1: - if (ftype == ::apache::thrift::protocol::T_I32) { - xfer += iprot->readI32(this->column_idx); - isset_column_idx = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 2: - if (ftype == ::apache::thrift::protocol::T_BOOL) { - xfer += iprot->readBool(this->descending); - isset_descending = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 3: - if (ftype == ::apache::thrift::protocol::T_BOOL) { - xfer += iprot->readBool(this->nulls_first); - isset_nulls_first = true; - } else { - xfer += iprot->skip(ftype); - } - break; - default: - xfer += iprot->skip(ftype); - break; - } - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - if (!isset_column_idx) - throw TProtocolException(TProtocolException::INVALID_DATA); - if (!isset_descending) - throw TProtocolException(TProtocolException::INVALID_DATA); - if (!isset_nulls_first) - throw TProtocolException(TProtocolException::INVALID_DATA); - return xfer; -} - -uint32_t SortingColumn::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("SortingColumn"); - - xfer += oprot->writeFieldBegin("column_idx", ::apache::thrift::protocol::T_I32, 1); - xfer += oprot->writeI32(this->column_idx); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldBegin("descending", ::apache::thrift::protocol::T_BOOL, 2); - xfer += oprot->writeBool(this->descending); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldBegin("nulls_first", ::apache::thrift::protocol::T_BOOL, 3); - xfer += oprot->writeBool(this->nulls_first); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(SortingColumn &a, SortingColumn &b) { - using ::std::swap; - swap(a.column_idx, b.column_idx); - swap(a.descending, b.descending); - swap(a.nulls_first, b.nulls_first); -} - -SortingColumn::SortingColumn(const SortingColumn& other75) { - column_idx = other75.column_idx; - descending = other75.descending; - nulls_first = other75.nulls_first; -} -SortingColumn& SortingColumn::operator=(const SortingColumn& other76) { - column_idx = other76.column_idx; - descending = other76.descending; - nulls_first = other76.nulls_first; - return *this; -} -void SortingColumn::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "SortingColumn("; - out << "column_idx=" << to_string(column_idx); - out << ", " << "descending=" << to_string(descending); - out << ", " << "nulls_first=" << to_string(nulls_first); - out << ")"; -} - - -PageEncodingStats::~PageEncodingStats() throw() { -} - - -void PageEncodingStats::__set_page_type(const PageType::type val) { - this->page_type = val; -} - -void PageEncodingStats::__set_encoding(const Encoding::type val) { - this->encoding = val; -} - -void PageEncodingStats::__set_count(const int32_t val) { - this->count = val; -} -std::ostream& operator<<(std::ostream& out, const PageEncodingStats& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t PageEncodingStats::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - bool isset_page_type = false; - bool isset_encoding = false; - bool isset_count = false; - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - switch (fid) - { - case 1: - if (ftype == ::apache::thrift::protocol::T_I32) { - int32_t ecast77; - xfer += iprot->readI32(ecast77); - this->page_type = (PageType::type)ecast77; - isset_page_type = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 2: - if (ftype == ::apache::thrift::protocol::T_I32) { - int32_t ecast78; - xfer += iprot->readI32(ecast78); - this->encoding = (Encoding::type)ecast78; - isset_encoding = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 3: - if (ftype == ::apache::thrift::protocol::T_I32) { - xfer += iprot->readI32(this->count); - isset_count = true; - } else { - xfer += iprot->skip(ftype); - } - break; - default: - xfer += iprot->skip(ftype); - break; - } - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - if (!isset_page_type) - throw TProtocolException(TProtocolException::INVALID_DATA); - if (!isset_encoding) - throw TProtocolException(TProtocolException::INVALID_DATA); - if (!isset_count) - throw TProtocolException(TProtocolException::INVALID_DATA); - return xfer; -} - -uint32_t PageEncodingStats::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("PageEncodingStats"); - - xfer += oprot->writeFieldBegin("page_type", ::apache::thrift::protocol::T_I32, 1); - xfer += oprot->writeI32((int32_t)this->page_type); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldBegin("encoding", ::apache::thrift::protocol::T_I32, 2); - xfer += oprot->writeI32((int32_t)this->encoding); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldBegin("count", ::apache::thrift::protocol::T_I32, 3); - xfer += oprot->writeI32(this->count); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(PageEncodingStats &a, PageEncodingStats &b) { - using ::std::swap; - swap(a.page_type, b.page_type); - swap(a.encoding, b.encoding); - swap(a.count, b.count); -} - -PageEncodingStats::PageEncodingStats(const PageEncodingStats& other79) { - page_type = other79.page_type; - encoding = other79.encoding; - count = other79.count; -} -PageEncodingStats& PageEncodingStats::operator=(const PageEncodingStats& other80) { - page_type = other80.page_type; - encoding = other80.encoding; - count = other80.count; - return *this; -} -void PageEncodingStats::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "PageEncodingStats("; - out << "page_type=" << to_string(page_type); - out << ", " << "encoding=" << to_string(encoding); - out << ", " << "count=" << to_string(count); - out << ")"; -} - - -ColumnMetaData::~ColumnMetaData() throw() { -} - - -void ColumnMetaData::__set_type(const Type::type val) { - this->type = val; -} - -void ColumnMetaData::__set_encodings(const std::vector & val) { - this->encodings = val; -} - -void ColumnMetaData::__set_path_in_schema(const std::vector & val) { - this->path_in_schema = val; -} - -void ColumnMetaData::__set_codec(const CompressionCodec::type val) { - this->codec = val; -} - -void ColumnMetaData::__set_num_values(const int64_t val) { - this->num_values = val; -} - -void ColumnMetaData::__set_total_uncompressed_size(const int64_t val) { - this->total_uncompressed_size = val; -} - -void ColumnMetaData::__set_total_compressed_size(const int64_t val) { - this->total_compressed_size = val; -} - -void ColumnMetaData::__set_key_value_metadata(const std::vector & val) { - this->key_value_metadata = val; -__isset.key_value_metadata = true; -} - -void ColumnMetaData::__set_data_page_offset(const int64_t val) { - this->data_page_offset = val; -} - -void ColumnMetaData::__set_index_page_offset(const int64_t val) { - this->index_page_offset = val; -__isset.index_page_offset = true; -} - -void ColumnMetaData::__set_dictionary_page_offset(const int64_t val) { - this->dictionary_page_offset = val; -__isset.dictionary_page_offset = true; -} - -void ColumnMetaData::__set_statistics(const Statistics& val) { - this->statistics = val; -__isset.statistics = true; -} - -void ColumnMetaData::__set_encoding_stats(const std::vector & val) { - this->encoding_stats = val; -__isset.encoding_stats = true; -} - -void ColumnMetaData::__set_bloom_filter_offset(const int64_t val) { - this->bloom_filter_offset = val; -__isset.bloom_filter_offset = true; -} -std::ostream& operator<<(std::ostream& out, const ColumnMetaData& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t ColumnMetaData::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - bool isset_type = false; - bool isset_encodings = false; - bool isset_path_in_schema = false; - bool isset_codec = false; - bool isset_num_values = false; - bool isset_total_uncompressed_size = false; - bool isset_total_compressed_size = false; - bool isset_data_page_offset = false; - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - switch (fid) - { - case 1: - if (ftype == ::apache::thrift::protocol::T_I32) { - int32_t ecast81; - xfer += iprot->readI32(ecast81); - this->type = (Type::type)ecast81; - isset_type = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 2: - if (ftype == ::apache::thrift::protocol::T_LIST) { - { - this->encodings.clear(); - uint32_t _size82; - ::apache::thrift::protocol::TType _etype85; - xfer += iprot->readListBegin(_etype85, _size82); - this->encodings.resize(_size82); - uint32_t _i86; - for (_i86 = 0; _i86 < _size82; ++_i86) - { - int32_t ecast87; - xfer += iprot->readI32(ecast87); - this->encodings[_i86] = (Encoding::type)ecast87; - } - xfer += iprot->readListEnd(); - } - isset_encodings = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 3: - if (ftype == ::apache::thrift::protocol::T_LIST) { - { - this->path_in_schema.clear(); - uint32_t _size88; - ::apache::thrift::protocol::TType _etype91; - xfer += iprot->readListBegin(_etype91, _size88); - this->path_in_schema.resize(_size88); - uint32_t _i92; - for (_i92 = 0; _i92 < _size88; ++_i92) - { - xfer += iprot->readString(this->path_in_schema[_i92]); - } - xfer += iprot->readListEnd(); - } - isset_path_in_schema = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 4: - if (ftype == ::apache::thrift::protocol::T_I32) { - int32_t ecast93; - xfer += iprot->readI32(ecast93); - this->codec = (CompressionCodec::type)ecast93; - isset_codec = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 5: - if (ftype == ::apache::thrift::protocol::T_I64) { - xfer += iprot->readI64(this->num_values); - isset_num_values = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 6: - if (ftype == ::apache::thrift::protocol::T_I64) { - xfer += iprot->readI64(this->total_uncompressed_size); - isset_total_uncompressed_size = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 7: - if (ftype == ::apache::thrift::protocol::T_I64) { - xfer += iprot->readI64(this->total_compressed_size); - isset_total_compressed_size = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 8: - if (ftype == ::apache::thrift::protocol::T_LIST) { - { - this->key_value_metadata.clear(); - uint32_t _size94; - ::apache::thrift::protocol::TType _etype97; - xfer += iprot->readListBegin(_etype97, _size94); - this->key_value_metadata.resize(_size94); - uint32_t _i98; - for (_i98 = 0; _i98 < _size94; ++_i98) - { - xfer += this->key_value_metadata[_i98].read(iprot); - } - xfer += iprot->readListEnd(); - } - this->__isset.key_value_metadata = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 9: - if (ftype == ::apache::thrift::protocol::T_I64) { - xfer += iprot->readI64(this->data_page_offset); - isset_data_page_offset = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 10: - if (ftype == ::apache::thrift::protocol::T_I64) { - xfer += iprot->readI64(this->index_page_offset); - this->__isset.index_page_offset = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 11: - if (ftype == ::apache::thrift::protocol::T_I64) { - xfer += iprot->readI64(this->dictionary_page_offset); - this->__isset.dictionary_page_offset = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 12: - if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->statistics.read(iprot); - this->__isset.statistics = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 13: - if (ftype == ::apache::thrift::protocol::T_LIST) { - { - this->encoding_stats.clear(); - uint32_t _size99; - ::apache::thrift::protocol::TType _etype102; - xfer += iprot->readListBegin(_etype102, _size99); - this->encoding_stats.resize(_size99); - uint32_t _i103; - for (_i103 = 0; _i103 < _size99; ++_i103) - { - xfer += this->encoding_stats[_i103].read(iprot); - } - xfer += iprot->readListEnd(); - } - this->__isset.encoding_stats = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 14: - if (ftype == ::apache::thrift::protocol::T_I64) { - xfer += iprot->readI64(this->bloom_filter_offset); - this->__isset.bloom_filter_offset = true; - } else { - xfer += iprot->skip(ftype); - } - break; - default: - xfer += iprot->skip(ftype); - break; - } - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - if (!isset_type) - throw TProtocolException(TProtocolException::INVALID_DATA); - if (!isset_encodings) - throw TProtocolException(TProtocolException::INVALID_DATA); - if (!isset_path_in_schema) - throw TProtocolException(TProtocolException::INVALID_DATA); - if (!isset_codec) - throw TProtocolException(TProtocolException::INVALID_DATA); - if (!isset_num_values) - throw TProtocolException(TProtocolException::INVALID_DATA); - if (!isset_total_uncompressed_size) - throw TProtocolException(TProtocolException::INVALID_DATA); - if (!isset_total_compressed_size) - throw TProtocolException(TProtocolException::INVALID_DATA); - if (!isset_data_page_offset) - throw TProtocolException(TProtocolException::INVALID_DATA); - return xfer; -} - -uint32_t ColumnMetaData::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("ColumnMetaData"); - - xfer += oprot->writeFieldBegin("type", ::apache::thrift::protocol::T_I32, 1); - xfer += oprot->writeI32((int32_t)this->type); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldBegin("encodings", ::apache::thrift::protocol::T_LIST, 2); - { - xfer += oprot->writeListBegin(::apache::thrift::protocol::T_I32, static_cast(this->encodings.size())); - std::vector ::const_iterator _iter104; - for (_iter104 = this->encodings.begin(); _iter104 != this->encodings.end(); ++_iter104) - { - xfer += oprot->writeI32((int32_t)(*_iter104)); - } - xfer += oprot->writeListEnd(); - } - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldBegin("path_in_schema", ::apache::thrift::protocol::T_LIST, 3); - { - xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRING, static_cast(this->path_in_schema.size())); - std::vector ::const_iterator _iter105; - for (_iter105 = this->path_in_schema.begin(); _iter105 != this->path_in_schema.end(); ++_iter105) - { - xfer += oprot->writeString((*_iter105)); - } - xfer += oprot->writeListEnd(); - } - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldBegin("codec", ::apache::thrift::protocol::T_I32, 4); - xfer += oprot->writeI32((int32_t)this->codec); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldBegin("num_values", ::apache::thrift::protocol::T_I64, 5); - xfer += oprot->writeI64(this->num_values); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldBegin("total_uncompressed_size", ::apache::thrift::protocol::T_I64, 6); - xfer += oprot->writeI64(this->total_uncompressed_size); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldBegin("total_compressed_size", ::apache::thrift::protocol::T_I64, 7); - xfer += oprot->writeI64(this->total_compressed_size); - xfer += oprot->writeFieldEnd(); - - if (this->__isset.key_value_metadata) { - xfer += oprot->writeFieldBegin("key_value_metadata", ::apache::thrift::protocol::T_LIST, 8); - { - xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, static_cast(this->key_value_metadata.size())); - std::vector ::const_iterator _iter106; - for (_iter106 = this->key_value_metadata.begin(); _iter106 != this->key_value_metadata.end(); ++_iter106) - { - xfer += (*_iter106).write(oprot); - } - xfer += oprot->writeListEnd(); - } - xfer += oprot->writeFieldEnd(); - } - xfer += oprot->writeFieldBegin("data_page_offset", ::apache::thrift::protocol::T_I64, 9); - xfer += oprot->writeI64(this->data_page_offset); - xfer += oprot->writeFieldEnd(); - - if (this->__isset.index_page_offset) { - xfer += oprot->writeFieldBegin("index_page_offset", ::apache::thrift::protocol::T_I64, 10); - xfer += oprot->writeI64(this->index_page_offset); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.dictionary_page_offset) { - xfer += oprot->writeFieldBegin("dictionary_page_offset", ::apache::thrift::protocol::T_I64, 11); - xfer += oprot->writeI64(this->dictionary_page_offset); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.statistics) { - xfer += oprot->writeFieldBegin("statistics", ::apache::thrift::protocol::T_STRUCT, 12); - xfer += this->statistics.write(oprot); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.encoding_stats) { - xfer += oprot->writeFieldBegin("encoding_stats", ::apache::thrift::protocol::T_LIST, 13); - { - xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, static_cast(this->encoding_stats.size())); - std::vector ::const_iterator _iter107; - for (_iter107 = this->encoding_stats.begin(); _iter107 != this->encoding_stats.end(); ++_iter107) - { - xfer += (*_iter107).write(oprot); - } - xfer += oprot->writeListEnd(); - } - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.bloom_filter_offset) { - xfer += oprot->writeFieldBegin("bloom_filter_offset", ::apache::thrift::protocol::T_I64, 14); - xfer += oprot->writeI64(this->bloom_filter_offset); - xfer += oprot->writeFieldEnd(); - } - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(ColumnMetaData &a, ColumnMetaData &b) { - using ::std::swap; - swap(a.type, b.type); - swap(a.encodings, b.encodings); - swap(a.path_in_schema, b.path_in_schema); - swap(a.codec, b.codec); - swap(a.num_values, b.num_values); - swap(a.total_uncompressed_size, b.total_uncompressed_size); - swap(a.total_compressed_size, b.total_compressed_size); - swap(a.key_value_metadata, b.key_value_metadata); - swap(a.data_page_offset, b.data_page_offset); - swap(a.index_page_offset, b.index_page_offset); - swap(a.dictionary_page_offset, b.dictionary_page_offset); - swap(a.statistics, b.statistics); - swap(a.encoding_stats, b.encoding_stats); - swap(a.bloom_filter_offset, b.bloom_filter_offset); - swap(a.__isset, b.__isset); -} - -ColumnMetaData::ColumnMetaData(const ColumnMetaData& other108) { - type = other108.type; - encodings = other108.encodings; - path_in_schema = other108.path_in_schema; - codec = other108.codec; - num_values = other108.num_values; - total_uncompressed_size = other108.total_uncompressed_size; - total_compressed_size = other108.total_compressed_size; - key_value_metadata = other108.key_value_metadata; - data_page_offset = other108.data_page_offset; - index_page_offset = other108.index_page_offset; - dictionary_page_offset = other108.dictionary_page_offset; - statistics = other108.statistics; - encoding_stats = other108.encoding_stats; - bloom_filter_offset = other108.bloom_filter_offset; - __isset = other108.__isset; -} -ColumnMetaData& ColumnMetaData::operator=(const ColumnMetaData& other109) { - type = other109.type; - encodings = other109.encodings; - path_in_schema = other109.path_in_schema; - codec = other109.codec; - num_values = other109.num_values; - total_uncompressed_size = other109.total_uncompressed_size; - total_compressed_size = other109.total_compressed_size; - key_value_metadata = other109.key_value_metadata; - data_page_offset = other109.data_page_offset; - index_page_offset = other109.index_page_offset; - dictionary_page_offset = other109.dictionary_page_offset; - statistics = other109.statistics; - encoding_stats = other109.encoding_stats; - bloom_filter_offset = other109.bloom_filter_offset; - __isset = other109.__isset; - return *this; -} -void ColumnMetaData::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "ColumnMetaData("; - out << "type=" << to_string(type); - out << ", " << "encodings=" << to_string(encodings); - out << ", " << "path_in_schema=" << to_string(path_in_schema); - out << ", " << "codec=" << to_string(codec); - out << ", " << "num_values=" << to_string(num_values); - out << ", " << "total_uncompressed_size=" << to_string(total_uncompressed_size); - out << ", " << "total_compressed_size=" << to_string(total_compressed_size); - out << ", " << "key_value_metadata="; (__isset.key_value_metadata ? (out << to_string(key_value_metadata)) : (out << "")); - out << ", " << "data_page_offset=" << to_string(data_page_offset); - out << ", " << "index_page_offset="; (__isset.index_page_offset ? (out << to_string(index_page_offset)) : (out << "")); - out << ", " << "dictionary_page_offset="; (__isset.dictionary_page_offset ? (out << to_string(dictionary_page_offset)) : (out << "")); - out << ", " << "statistics="; (__isset.statistics ? (out << to_string(statistics)) : (out << "")); - out << ", " << "encoding_stats="; (__isset.encoding_stats ? (out << to_string(encoding_stats)) : (out << "")); - out << ", " << "bloom_filter_offset="; (__isset.bloom_filter_offset ? (out << to_string(bloom_filter_offset)) : (out << "")); - out << ")"; -} - - -EncryptionWithFooterKey::~EncryptionWithFooterKey() throw() { -} - -std::ostream& operator<<(std::ostream& out, const EncryptionWithFooterKey& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t EncryptionWithFooterKey::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - xfer += iprot->skip(ftype); - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - return xfer; -} - -uint32_t EncryptionWithFooterKey::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("EncryptionWithFooterKey"); - - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(EncryptionWithFooterKey &a, EncryptionWithFooterKey &b) { - using ::std::swap; - (void) a; - (void) b; -} - -EncryptionWithFooterKey::EncryptionWithFooterKey(const EncryptionWithFooterKey& other110) { - (void) other110; -} -EncryptionWithFooterKey& EncryptionWithFooterKey::operator=(const EncryptionWithFooterKey& other111) { - (void) other111; - return *this; -} -void EncryptionWithFooterKey::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "EncryptionWithFooterKey("; - out << ")"; -} - - -EncryptionWithColumnKey::~EncryptionWithColumnKey() throw() { -} - - -void EncryptionWithColumnKey::__set_path_in_schema(const std::vector & val) { - this->path_in_schema = val; -} - -void EncryptionWithColumnKey::__set_key_metadata(const std::string& val) { - this->key_metadata = val; -__isset.key_metadata = true; -} -std::ostream& operator<<(std::ostream& out, const EncryptionWithColumnKey& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t EncryptionWithColumnKey::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - bool isset_path_in_schema = false; - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - switch (fid) - { - case 1: - if (ftype == ::apache::thrift::protocol::T_LIST) { - { - this->path_in_schema.clear(); - uint32_t _size112; - ::apache::thrift::protocol::TType _etype115; - xfer += iprot->readListBegin(_etype115, _size112); - this->path_in_schema.resize(_size112); - uint32_t _i116; - for (_i116 = 0; _i116 < _size112; ++_i116) - { - xfer += iprot->readString(this->path_in_schema[_i116]); - } - xfer += iprot->readListEnd(); - } - isset_path_in_schema = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 2: - if (ftype == ::apache::thrift::protocol::T_STRING) { - xfer += iprot->readBinary(this->key_metadata); - this->__isset.key_metadata = true; - } else { - xfer += iprot->skip(ftype); - } - break; - default: - xfer += iprot->skip(ftype); - break; - } - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - if (!isset_path_in_schema) - throw TProtocolException(TProtocolException::INVALID_DATA); - return xfer; -} - -uint32_t EncryptionWithColumnKey::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("EncryptionWithColumnKey"); - - xfer += oprot->writeFieldBegin("path_in_schema", ::apache::thrift::protocol::T_LIST, 1); - { - xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRING, static_cast(this->path_in_schema.size())); - std::vector ::const_iterator _iter117; - for (_iter117 = this->path_in_schema.begin(); _iter117 != this->path_in_schema.end(); ++_iter117) - { - xfer += oprot->writeString((*_iter117)); - } - xfer += oprot->writeListEnd(); - } - xfer += oprot->writeFieldEnd(); - - if (this->__isset.key_metadata) { - xfer += oprot->writeFieldBegin("key_metadata", ::apache::thrift::protocol::T_STRING, 2); - xfer += oprot->writeBinary(this->key_metadata); - xfer += oprot->writeFieldEnd(); - } - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(EncryptionWithColumnKey &a, EncryptionWithColumnKey &b) { - using ::std::swap; - swap(a.path_in_schema, b.path_in_schema); - swap(a.key_metadata, b.key_metadata); - swap(a.__isset, b.__isset); -} - -EncryptionWithColumnKey::EncryptionWithColumnKey(const EncryptionWithColumnKey& other118) { - path_in_schema = other118.path_in_schema; - key_metadata = other118.key_metadata; - __isset = other118.__isset; -} -EncryptionWithColumnKey& EncryptionWithColumnKey::operator=(const EncryptionWithColumnKey& other119) { - path_in_schema = other119.path_in_schema; - key_metadata = other119.key_metadata; - __isset = other119.__isset; - return *this; -} -void EncryptionWithColumnKey::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "EncryptionWithColumnKey("; - out << "path_in_schema=" << to_string(path_in_schema); - out << ", " << "key_metadata="; (__isset.key_metadata ? (out << to_string(key_metadata)) : (out << "")); - out << ")"; -} - - -ColumnCryptoMetaData::~ColumnCryptoMetaData() throw() { -} - - -void ColumnCryptoMetaData::__set_ENCRYPTION_WITH_FOOTER_KEY(const EncryptionWithFooterKey& val) { - this->ENCRYPTION_WITH_FOOTER_KEY = val; -__isset.ENCRYPTION_WITH_FOOTER_KEY = true; -} - -void ColumnCryptoMetaData::__set_ENCRYPTION_WITH_COLUMN_KEY(const EncryptionWithColumnKey& val) { - this->ENCRYPTION_WITH_COLUMN_KEY = val; -__isset.ENCRYPTION_WITH_COLUMN_KEY = true; -} -std::ostream& operator<<(std::ostream& out, const ColumnCryptoMetaData& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t ColumnCryptoMetaData::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - switch (fid) - { - case 1: - if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->ENCRYPTION_WITH_FOOTER_KEY.read(iprot); - this->__isset.ENCRYPTION_WITH_FOOTER_KEY = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 2: - if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->ENCRYPTION_WITH_COLUMN_KEY.read(iprot); - this->__isset.ENCRYPTION_WITH_COLUMN_KEY = true; - } else { - xfer += iprot->skip(ftype); - } - break; - default: - xfer += iprot->skip(ftype); - break; - } - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - return xfer; -} - -uint32_t ColumnCryptoMetaData::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("ColumnCryptoMetaData"); - - if (this->__isset.ENCRYPTION_WITH_FOOTER_KEY) { - xfer += oprot->writeFieldBegin("ENCRYPTION_WITH_FOOTER_KEY", ::apache::thrift::protocol::T_STRUCT, 1); - xfer += this->ENCRYPTION_WITH_FOOTER_KEY.write(oprot); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.ENCRYPTION_WITH_COLUMN_KEY) { - xfer += oprot->writeFieldBegin("ENCRYPTION_WITH_COLUMN_KEY", ::apache::thrift::protocol::T_STRUCT, 2); - xfer += this->ENCRYPTION_WITH_COLUMN_KEY.write(oprot); - xfer += oprot->writeFieldEnd(); - } - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(ColumnCryptoMetaData &a, ColumnCryptoMetaData &b) { - using ::std::swap; - swap(a.ENCRYPTION_WITH_FOOTER_KEY, b.ENCRYPTION_WITH_FOOTER_KEY); - swap(a.ENCRYPTION_WITH_COLUMN_KEY, b.ENCRYPTION_WITH_COLUMN_KEY); - swap(a.__isset, b.__isset); -} - -ColumnCryptoMetaData::ColumnCryptoMetaData(const ColumnCryptoMetaData& other120) { - ENCRYPTION_WITH_FOOTER_KEY = other120.ENCRYPTION_WITH_FOOTER_KEY; - ENCRYPTION_WITH_COLUMN_KEY = other120.ENCRYPTION_WITH_COLUMN_KEY; - __isset = other120.__isset; -} -ColumnCryptoMetaData& ColumnCryptoMetaData::operator=(const ColumnCryptoMetaData& other121) { - ENCRYPTION_WITH_FOOTER_KEY = other121.ENCRYPTION_WITH_FOOTER_KEY; - ENCRYPTION_WITH_COLUMN_KEY = other121.ENCRYPTION_WITH_COLUMN_KEY; - __isset = other121.__isset; - return *this; -} -void ColumnCryptoMetaData::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "ColumnCryptoMetaData("; - out << "ENCRYPTION_WITH_FOOTER_KEY="; (__isset.ENCRYPTION_WITH_FOOTER_KEY ? (out << to_string(ENCRYPTION_WITH_FOOTER_KEY)) : (out << "")); - out << ", " << "ENCRYPTION_WITH_COLUMN_KEY="; (__isset.ENCRYPTION_WITH_COLUMN_KEY ? (out << to_string(ENCRYPTION_WITH_COLUMN_KEY)) : (out << "")); - out << ")"; -} - - -ColumnChunk::~ColumnChunk() throw() { -} - - -void ColumnChunk::__set_file_path(const std::string& val) { - this->file_path = val; -__isset.file_path = true; -} - -void ColumnChunk::__set_file_offset(const int64_t val) { - this->file_offset = val; -} - -void ColumnChunk::__set_meta_data(const ColumnMetaData& val) { - this->meta_data = val; -__isset.meta_data = true; -} - -void ColumnChunk::__set_offset_index_offset(const int64_t val) { - this->offset_index_offset = val; -__isset.offset_index_offset = true; -} - -void ColumnChunk::__set_offset_index_length(const int32_t val) { - this->offset_index_length = val; -__isset.offset_index_length = true; -} - -void ColumnChunk::__set_column_index_offset(const int64_t val) { - this->column_index_offset = val; -__isset.column_index_offset = true; -} - -void ColumnChunk::__set_column_index_length(const int32_t val) { - this->column_index_length = val; -__isset.column_index_length = true; -} - -void ColumnChunk::__set_crypto_metadata(const ColumnCryptoMetaData& val) { - this->crypto_metadata = val; -__isset.crypto_metadata = true; -} - -void ColumnChunk::__set_encrypted_column_metadata(const std::string& val) { - this->encrypted_column_metadata = val; -__isset.encrypted_column_metadata = true; -} -std::ostream& operator<<(std::ostream& out, const ColumnChunk& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t ColumnChunk::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - bool isset_file_offset = false; - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - switch (fid) - { - case 1: - if (ftype == ::apache::thrift::protocol::T_STRING) { - xfer += iprot->readString(this->file_path); - this->__isset.file_path = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 2: - if (ftype == ::apache::thrift::protocol::T_I64) { - xfer += iprot->readI64(this->file_offset); - isset_file_offset = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 3: - if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->meta_data.read(iprot); - this->__isset.meta_data = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 4: - if (ftype == ::apache::thrift::protocol::T_I64) { - xfer += iprot->readI64(this->offset_index_offset); - this->__isset.offset_index_offset = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 5: - if (ftype == ::apache::thrift::protocol::T_I32) { - xfer += iprot->readI32(this->offset_index_length); - this->__isset.offset_index_length = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 6: - if (ftype == ::apache::thrift::protocol::T_I64) { - xfer += iprot->readI64(this->column_index_offset); - this->__isset.column_index_offset = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 7: - if (ftype == ::apache::thrift::protocol::T_I32) { - xfer += iprot->readI32(this->column_index_length); - this->__isset.column_index_length = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 8: - if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->crypto_metadata.read(iprot); - this->__isset.crypto_metadata = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 9: - if (ftype == ::apache::thrift::protocol::T_STRING) { - xfer += iprot->readBinary(this->encrypted_column_metadata); - this->__isset.encrypted_column_metadata = true; - } else { - xfer += iprot->skip(ftype); - } - break; - default: - xfer += iprot->skip(ftype); - break; - } - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - if (!isset_file_offset) - throw TProtocolException(TProtocolException::INVALID_DATA); - return xfer; -} - -uint32_t ColumnChunk::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("ColumnChunk"); - - if (this->__isset.file_path) { - xfer += oprot->writeFieldBegin("file_path", ::apache::thrift::protocol::T_STRING, 1); - xfer += oprot->writeString(this->file_path); - xfer += oprot->writeFieldEnd(); - } - xfer += oprot->writeFieldBegin("file_offset", ::apache::thrift::protocol::T_I64, 2); - xfer += oprot->writeI64(this->file_offset); - xfer += oprot->writeFieldEnd(); - - if (this->__isset.meta_data) { - xfer += oprot->writeFieldBegin("meta_data", ::apache::thrift::protocol::T_STRUCT, 3); - xfer += this->meta_data.write(oprot); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.offset_index_offset) { - xfer += oprot->writeFieldBegin("offset_index_offset", ::apache::thrift::protocol::T_I64, 4); - xfer += oprot->writeI64(this->offset_index_offset); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.offset_index_length) { - xfer += oprot->writeFieldBegin("offset_index_length", ::apache::thrift::protocol::T_I32, 5); - xfer += oprot->writeI32(this->offset_index_length); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.column_index_offset) { - xfer += oprot->writeFieldBegin("column_index_offset", ::apache::thrift::protocol::T_I64, 6); - xfer += oprot->writeI64(this->column_index_offset); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.column_index_length) { - xfer += oprot->writeFieldBegin("column_index_length", ::apache::thrift::protocol::T_I32, 7); - xfer += oprot->writeI32(this->column_index_length); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.crypto_metadata) { - xfer += oprot->writeFieldBegin("crypto_metadata", ::apache::thrift::protocol::T_STRUCT, 8); - xfer += this->crypto_metadata.write(oprot); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.encrypted_column_metadata) { - xfer += oprot->writeFieldBegin("encrypted_column_metadata", ::apache::thrift::protocol::T_STRING, 9); - xfer += oprot->writeBinary(this->encrypted_column_metadata); - xfer += oprot->writeFieldEnd(); - } - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(ColumnChunk &a, ColumnChunk &b) { - using ::std::swap; - swap(a.file_path, b.file_path); - swap(a.file_offset, b.file_offset); - swap(a.meta_data, b.meta_data); - swap(a.offset_index_offset, b.offset_index_offset); - swap(a.offset_index_length, b.offset_index_length); - swap(a.column_index_offset, b.column_index_offset); - swap(a.column_index_length, b.column_index_length); - swap(a.crypto_metadata, b.crypto_metadata); - swap(a.encrypted_column_metadata, b.encrypted_column_metadata); - swap(a.__isset, b.__isset); -} - -ColumnChunk::ColumnChunk(const ColumnChunk& other122) { - file_path = other122.file_path; - file_offset = other122.file_offset; - meta_data = other122.meta_data; - offset_index_offset = other122.offset_index_offset; - offset_index_length = other122.offset_index_length; - column_index_offset = other122.column_index_offset; - column_index_length = other122.column_index_length; - crypto_metadata = other122.crypto_metadata; - encrypted_column_metadata = other122.encrypted_column_metadata; - __isset = other122.__isset; -} -ColumnChunk& ColumnChunk::operator=(const ColumnChunk& other123) { - file_path = other123.file_path; - file_offset = other123.file_offset; - meta_data = other123.meta_data; - offset_index_offset = other123.offset_index_offset; - offset_index_length = other123.offset_index_length; - column_index_offset = other123.column_index_offset; - column_index_length = other123.column_index_length; - crypto_metadata = other123.crypto_metadata; - encrypted_column_metadata = other123.encrypted_column_metadata; - __isset = other123.__isset; - return *this; -} -void ColumnChunk::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "ColumnChunk("; - out << "file_path="; (__isset.file_path ? (out << to_string(file_path)) : (out << "")); - out << ", " << "file_offset=" << to_string(file_offset); - out << ", " << "meta_data="; (__isset.meta_data ? (out << to_string(meta_data)) : (out << "")); - out << ", " << "offset_index_offset="; (__isset.offset_index_offset ? (out << to_string(offset_index_offset)) : (out << "")); - out << ", " << "offset_index_length="; (__isset.offset_index_length ? (out << to_string(offset_index_length)) : (out << "")); - out << ", " << "column_index_offset="; (__isset.column_index_offset ? (out << to_string(column_index_offset)) : (out << "")); - out << ", " << "column_index_length="; (__isset.column_index_length ? (out << to_string(column_index_length)) : (out << "")); - out << ", " << "crypto_metadata="; (__isset.crypto_metadata ? (out << to_string(crypto_metadata)) : (out << "")); - out << ", " << "encrypted_column_metadata="; (__isset.encrypted_column_metadata ? (out << to_string(encrypted_column_metadata)) : (out << "")); - out << ")"; -} - - -RowGroup::~RowGroup() throw() { -} - - -void RowGroup::__set_columns(const std::vector & val) { - this->columns = val; -} - -void RowGroup::__set_total_byte_size(const int64_t val) { - this->total_byte_size = val; -} - -void RowGroup::__set_num_rows(const int64_t val) { - this->num_rows = val; -} - -void RowGroup::__set_sorting_columns(const std::vector & val) { - this->sorting_columns = val; -__isset.sorting_columns = true; -} - -void RowGroup::__set_file_offset(const int64_t val) { - this->file_offset = val; -__isset.file_offset = true; -} - -void RowGroup::__set_total_compressed_size(const int64_t val) { - this->total_compressed_size = val; -__isset.total_compressed_size = true; -} - -void RowGroup::__set_ordinal(const int16_t val) { - this->ordinal = val; -__isset.ordinal = true; -} -std::ostream& operator<<(std::ostream& out, const RowGroup& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t RowGroup::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - bool isset_columns = false; - bool isset_total_byte_size = false; - bool isset_num_rows = false; - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - switch (fid) - { - case 1: - if (ftype == ::apache::thrift::protocol::T_LIST) { - { - this->columns.clear(); - uint32_t _size124; - ::apache::thrift::protocol::TType _etype127; - xfer += iprot->readListBegin(_etype127, _size124); - this->columns.resize(_size124); - uint32_t _i128; - for (_i128 = 0; _i128 < _size124; ++_i128) - { - xfer += this->columns[_i128].read(iprot); - } - xfer += iprot->readListEnd(); - } - isset_columns = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 2: - if (ftype == ::apache::thrift::protocol::T_I64) { - xfer += iprot->readI64(this->total_byte_size); - isset_total_byte_size = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 3: - if (ftype == ::apache::thrift::protocol::T_I64) { - xfer += iprot->readI64(this->num_rows); - isset_num_rows = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 4: - if (ftype == ::apache::thrift::protocol::T_LIST) { - { - this->sorting_columns.clear(); - uint32_t _size129; - ::apache::thrift::protocol::TType _etype132; - xfer += iprot->readListBegin(_etype132, _size129); - this->sorting_columns.resize(_size129); - uint32_t _i133; - for (_i133 = 0; _i133 < _size129; ++_i133) - { - xfer += this->sorting_columns[_i133].read(iprot); - } - xfer += iprot->readListEnd(); - } - this->__isset.sorting_columns = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 5: - if (ftype == ::apache::thrift::protocol::T_I64) { - xfer += iprot->readI64(this->file_offset); - this->__isset.file_offset = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 6: - if (ftype == ::apache::thrift::protocol::T_I64) { - xfer += iprot->readI64(this->total_compressed_size); - this->__isset.total_compressed_size = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 7: - if (ftype == ::apache::thrift::protocol::T_I16) { - xfer += iprot->readI16(this->ordinal); - this->__isset.ordinal = true; - } else { - xfer += iprot->skip(ftype); - } - break; - default: - xfer += iprot->skip(ftype); - break; - } - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - if (!isset_columns) - throw TProtocolException(TProtocolException::INVALID_DATA); - if (!isset_total_byte_size) - throw TProtocolException(TProtocolException::INVALID_DATA); - if (!isset_num_rows) - throw TProtocolException(TProtocolException::INVALID_DATA); - return xfer; -} - -uint32_t RowGroup::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("RowGroup"); - - xfer += oprot->writeFieldBegin("columns", ::apache::thrift::protocol::T_LIST, 1); - { - xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, static_cast(this->columns.size())); - std::vector ::const_iterator _iter134; - for (_iter134 = this->columns.begin(); _iter134 != this->columns.end(); ++_iter134) - { - xfer += (*_iter134).write(oprot); - } - xfer += oprot->writeListEnd(); - } - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldBegin("total_byte_size", ::apache::thrift::protocol::T_I64, 2); - xfer += oprot->writeI64(this->total_byte_size); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldBegin("num_rows", ::apache::thrift::protocol::T_I64, 3); - xfer += oprot->writeI64(this->num_rows); - xfer += oprot->writeFieldEnd(); - - if (this->__isset.sorting_columns) { - xfer += oprot->writeFieldBegin("sorting_columns", ::apache::thrift::protocol::T_LIST, 4); - { - xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, static_cast(this->sorting_columns.size())); - std::vector ::const_iterator _iter135; - for (_iter135 = this->sorting_columns.begin(); _iter135 != this->sorting_columns.end(); ++_iter135) - { - xfer += (*_iter135).write(oprot); - } - xfer += oprot->writeListEnd(); - } - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.file_offset) { - xfer += oprot->writeFieldBegin("file_offset", ::apache::thrift::protocol::T_I64, 5); - xfer += oprot->writeI64(this->file_offset); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.total_compressed_size) { - xfer += oprot->writeFieldBegin("total_compressed_size", ::apache::thrift::protocol::T_I64, 6); - xfer += oprot->writeI64(this->total_compressed_size); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.ordinal) { - xfer += oprot->writeFieldBegin("ordinal", ::apache::thrift::protocol::T_I16, 7); - xfer += oprot->writeI16(this->ordinal); - xfer += oprot->writeFieldEnd(); - } - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(RowGroup &a, RowGroup &b) { - using ::std::swap; - swap(a.columns, b.columns); - swap(a.total_byte_size, b.total_byte_size); - swap(a.num_rows, b.num_rows); - swap(a.sorting_columns, b.sorting_columns); - swap(a.file_offset, b.file_offset); - swap(a.total_compressed_size, b.total_compressed_size); - swap(a.ordinal, b.ordinal); - swap(a.__isset, b.__isset); -} - -RowGroup::RowGroup(const RowGroup& other136) { - columns = other136.columns; - total_byte_size = other136.total_byte_size; - num_rows = other136.num_rows; - sorting_columns = other136.sorting_columns; - file_offset = other136.file_offset; - total_compressed_size = other136.total_compressed_size; - ordinal = other136.ordinal; - __isset = other136.__isset; -} -RowGroup& RowGroup::operator=(const RowGroup& other137) { - columns = other137.columns; - total_byte_size = other137.total_byte_size; - num_rows = other137.num_rows; - sorting_columns = other137.sorting_columns; - file_offset = other137.file_offset; - total_compressed_size = other137.total_compressed_size; - ordinal = other137.ordinal; - __isset = other137.__isset; - return *this; -} -void RowGroup::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "RowGroup("; - out << "columns=" << to_string(columns); - out << ", " << "total_byte_size=" << to_string(total_byte_size); - out << ", " << "num_rows=" << to_string(num_rows); - out << ", " << "sorting_columns="; (__isset.sorting_columns ? (out << to_string(sorting_columns)) : (out << "")); - out << ", " << "file_offset="; (__isset.file_offset ? (out << to_string(file_offset)) : (out << "")); - out << ", " << "total_compressed_size="; (__isset.total_compressed_size ? (out << to_string(total_compressed_size)) : (out << "")); - out << ", " << "ordinal="; (__isset.ordinal ? (out << to_string(ordinal)) : (out << "")); - out << ")"; -} - - -TypeDefinedOrder::~TypeDefinedOrder() throw() { -} - -std::ostream& operator<<(std::ostream& out, const TypeDefinedOrder& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t TypeDefinedOrder::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - xfer += iprot->skip(ftype); - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - return xfer; -} - -uint32_t TypeDefinedOrder::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("TypeDefinedOrder"); - - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(TypeDefinedOrder &a, TypeDefinedOrder &b) { - using ::std::swap; - (void) a; - (void) b; -} - -TypeDefinedOrder::TypeDefinedOrder(const TypeDefinedOrder& other138) { - (void) other138; -} -TypeDefinedOrder& TypeDefinedOrder::operator=(const TypeDefinedOrder& other139) { - (void) other139; - return *this; -} -void TypeDefinedOrder::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "TypeDefinedOrder("; - out << ")"; -} - - -ColumnOrder::~ColumnOrder() throw() { -} - - -void ColumnOrder::__set_TYPE_ORDER(const TypeDefinedOrder& val) { - this->TYPE_ORDER = val; -__isset.TYPE_ORDER = true; -} -std::ostream& operator<<(std::ostream& out, const ColumnOrder& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t ColumnOrder::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - switch (fid) - { - case 1: - if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->TYPE_ORDER.read(iprot); - this->__isset.TYPE_ORDER = true; - } else { - xfer += iprot->skip(ftype); - } - break; - default: - xfer += iprot->skip(ftype); - break; - } - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - return xfer; -} - -uint32_t ColumnOrder::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("ColumnOrder"); - - if (this->__isset.TYPE_ORDER) { - xfer += oprot->writeFieldBegin("TYPE_ORDER", ::apache::thrift::protocol::T_STRUCT, 1); - xfer += this->TYPE_ORDER.write(oprot); - xfer += oprot->writeFieldEnd(); - } - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(ColumnOrder &a, ColumnOrder &b) { - using ::std::swap; - swap(a.TYPE_ORDER, b.TYPE_ORDER); - swap(a.__isset, b.__isset); -} - -ColumnOrder::ColumnOrder(const ColumnOrder& other140) { - TYPE_ORDER = other140.TYPE_ORDER; - __isset = other140.__isset; -} -ColumnOrder& ColumnOrder::operator=(const ColumnOrder& other141) { - TYPE_ORDER = other141.TYPE_ORDER; - __isset = other141.__isset; - return *this; -} -void ColumnOrder::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "ColumnOrder("; - out << "TYPE_ORDER="; (__isset.TYPE_ORDER ? (out << to_string(TYPE_ORDER)) : (out << "")); - out << ")"; -} - - -PageLocation::~PageLocation() throw() { -} - - -void PageLocation::__set_offset(const int64_t val) { - this->offset = val; -} - -void PageLocation::__set_compressed_page_size(const int32_t val) { - this->compressed_page_size = val; -} - -void PageLocation::__set_first_row_index(const int64_t val) { - this->first_row_index = val; -} -std::ostream& operator<<(std::ostream& out, const PageLocation& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t PageLocation::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - bool isset_offset = false; - bool isset_compressed_page_size = false; - bool isset_first_row_index = false; - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - switch (fid) - { - case 1: - if (ftype == ::apache::thrift::protocol::T_I64) { - xfer += iprot->readI64(this->offset); - isset_offset = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 2: - if (ftype == ::apache::thrift::protocol::T_I32) { - xfer += iprot->readI32(this->compressed_page_size); - isset_compressed_page_size = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 3: - if (ftype == ::apache::thrift::protocol::T_I64) { - xfer += iprot->readI64(this->first_row_index); - isset_first_row_index = true; - } else { - xfer += iprot->skip(ftype); - } - break; - default: - xfer += iprot->skip(ftype); - break; - } - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - if (!isset_offset) - throw TProtocolException(TProtocolException::INVALID_DATA); - if (!isset_compressed_page_size) - throw TProtocolException(TProtocolException::INVALID_DATA); - if (!isset_first_row_index) - throw TProtocolException(TProtocolException::INVALID_DATA); - return xfer; -} - -uint32_t PageLocation::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("PageLocation"); - - xfer += oprot->writeFieldBegin("offset", ::apache::thrift::protocol::T_I64, 1); - xfer += oprot->writeI64(this->offset); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldBegin("compressed_page_size", ::apache::thrift::protocol::T_I32, 2); - xfer += oprot->writeI32(this->compressed_page_size); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldBegin("first_row_index", ::apache::thrift::protocol::T_I64, 3); - xfer += oprot->writeI64(this->first_row_index); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(PageLocation &a, PageLocation &b) { - using ::std::swap; - swap(a.offset, b.offset); - swap(a.compressed_page_size, b.compressed_page_size); - swap(a.first_row_index, b.first_row_index); -} - -PageLocation::PageLocation(const PageLocation& other142) { - offset = other142.offset; - compressed_page_size = other142.compressed_page_size; - first_row_index = other142.first_row_index; -} -PageLocation& PageLocation::operator=(const PageLocation& other143) { - offset = other143.offset; - compressed_page_size = other143.compressed_page_size; - first_row_index = other143.first_row_index; - return *this; -} -void PageLocation::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "PageLocation("; - out << "offset=" << to_string(offset); - out << ", " << "compressed_page_size=" << to_string(compressed_page_size); - out << ", " << "first_row_index=" << to_string(first_row_index); - out << ")"; -} - - -OffsetIndex::~OffsetIndex() throw() { -} - - -void OffsetIndex::__set_page_locations(const std::vector & val) { - this->page_locations = val; -} -std::ostream& operator<<(std::ostream& out, const OffsetIndex& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t OffsetIndex::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - bool isset_page_locations = false; - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - switch (fid) - { - case 1: - if (ftype == ::apache::thrift::protocol::T_LIST) { - { - this->page_locations.clear(); - uint32_t _size144; - ::apache::thrift::protocol::TType _etype147; - xfer += iprot->readListBegin(_etype147, _size144); - this->page_locations.resize(_size144); - uint32_t _i148; - for (_i148 = 0; _i148 < _size144; ++_i148) - { - xfer += this->page_locations[_i148].read(iprot); - } - xfer += iprot->readListEnd(); - } - isset_page_locations = true; - } else { - xfer += iprot->skip(ftype); - } - break; - default: - xfer += iprot->skip(ftype); - break; - } - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - if (!isset_page_locations) - throw TProtocolException(TProtocolException::INVALID_DATA); - return xfer; -} - -uint32_t OffsetIndex::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("OffsetIndex"); - - xfer += oprot->writeFieldBegin("page_locations", ::apache::thrift::protocol::T_LIST, 1); - { - xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, static_cast(this->page_locations.size())); - std::vector ::const_iterator _iter149; - for (_iter149 = this->page_locations.begin(); _iter149 != this->page_locations.end(); ++_iter149) - { - xfer += (*_iter149).write(oprot); - } - xfer += oprot->writeListEnd(); - } - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(OffsetIndex &a, OffsetIndex &b) { - using ::std::swap; - swap(a.page_locations, b.page_locations); -} - -OffsetIndex::OffsetIndex(const OffsetIndex& other150) { - page_locations = other150.page_locations; -} -OffsetIndex& OffsetIndex::operator=(const OffsetIndex& other151) { - page_locations = other151.page_locations; - return *this; -} -void OffsetIndex::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "OffsetIndex("; - out << "page_locations=" << to_string(page_locations); - out << ")"; -} - - -ColumnIndex::~ColumnIndex() throw() { -} - - -void ColumnIndex::__set_null_pages(const std::vector & val) { - this->null_pages = val; -} - -void ColumnIndex::__set_min_values(const std::vector & val) { - this->min_values = val; -} - -void ColumnIndex::__set_max_values(const std::vector & val) { - this->max_values = val; -} - -void ColumnIndex::__set_boundary_order(const BoundaryOrder::type val) { - this->boundary_order = val; -} - -void ColumnIndex::__set_null_counts(const std::vector & val) { - this->null_counts = val; -__isset.null_counts = true; -} -std::ostream& operator<<(std::ostream& out, const ColumnIndex& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t ColumnIndex::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - bool isset_null_pages = false; - bool isset_min_values = false; - bool isset_max_values = false; - bool isset_boundary_order = false; - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - switch (fid) - { - case 1: - if (ftype == ::apache::thrift::protocol::T_LIST) { - { - this->null_pages.clear(); - uint32_t _size152; - ::apache::thrift::protocol::TType _etype155; - xfer += iprot->readListBegin(_etype155, _size152); - this->null_pages.resize(_size152); - uint32_t _i156; - for (_i156 = 0; _i156 < _size152; ++_i156) - { - xfer += iprot->readBool(this->null_pages[_i156]); - } - xfer += iprot->readListEnd(); - } - isset_null_pages = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 2: - if (ftype == ::apache::thrift::protocol::T_LIST) { - { - this->min_values.clear(); - uint32_t _size157; - ::apache::thrift::protocol::TType _etype160; - xfer += iprot->readListBegin(_etype160, _size157); - this->min_values.resize(_size157); - uint32_t _i161; - for (_i161 = 0; _i161 < _size157; ++_i161) - { - xfer += iprot->readBinary(this->min_values[_i161]); - } - xfer += iprot->readListEnd(); - } - isset_min_values = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 3: - if (ftype == ::apache::thrift::protocol::T_LIST) { - { - this->max_values.clear(); - uint32_t _size162; - ::apache::thrift::protocol::TType _etype165; - xfer += iprot->readListBegin(_etype165, _size162); - this->max_values.resize(_size162); - uint32_t _i166; - for (_i166 = 0; _i166 < _size162; ++_i166) - { - xfer += iprot->readBinary(this->max_values[_i166]); - } - xfer += iprot->readListEnd(); - } - isset_max_values = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 4: - if (ftype == ::apache::thrift::protocol::T_I32) { - int32_t ecast167; - xfer += iprot->readI32(ecast167); - this->boundary_order = (BoundaryOrder::type)ecast167; - isset_boundary_order = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 5: - if (ftype == ::apache::thrift::protocol::T_LIST) { - { - this->null_counts.clear(); - uint32_t _size168; - ::apache::thrift::protocol::TType _etype171; - xfer += iprot->readListBegin(_etype171, _size168); - this->null_counts.resize(_size168); - uint32_t _i172; - for (_i172 = 0; _i172 < _size168; ++_i172) - { - xfer += iprot->readI64(this->null_counts[_i172]); - } - xfer += iprot->readListEnd(); - } - this->__isset.null_counts = true; - } else { - xfer += iprot->skip(ftype); - } - break; - default: - xfer += iprot->skip(ftype); - break; - } - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - if (!isset_null_pages) - throw TProtocolException(TProtocolException::INVALID_DATA); - if (!isset_min_values) - throw TProtocolException(TProtocolException::INVALID_DATA); - if (!isset_max_values) - throw TProtocolException(TProtocolException::INVALID_DATA); - if (!isset_boundary_order) - throw TProtocolException(TProtocolException::INVALID_DATA); - return xfer; -} - -uint32_t ColumnIndex::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("ColumnIndex"); - - xfer += oprot->writeFieldBegin("null_pages", ::apache::thrift::protocol::T_LIST, 1); - { - xfer += oprot->writeListBegin(::apache::thrift::protocol::T_BOOL, static_cast(this->null_pages.size())); - std::vector ::const_iterator _iter173; - for (_iter173 = this->null_pages.begin(); _iter173 != this->null_pages.end(); ++_iter173) - { - xfer += oprot->writeBool((*_iter173)); - } - xfer += oprot->writeListEnd(); - } - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldBegin("min_values", ::apache::thrift::protocol::T_LIST, 2); - { - xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRING, static_cast(this->min_values.size())); - std::vector ::const_iterator _iter174; - for (_iter174 = this->min_values.begin(); _iter174 != this->min_values.end(); ++_iter174) - { - xfer += oprot->writeBinary((*_iter174)); - } - xfer += oprot->writeListEnd(); - } - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldBegin("max_values", ::apache::thrift::protocol::T_LIST, 3); - { - xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRING, static_cast(this->max_values.size())); - std::vector ::const_iterator _iter175; - for (_iter175 = this->max_values.begin(); _iter175 != this->max_values.end(); ++_iter175) - { - xfer += oprot->writeBinary((*_iter175)); - } - xfer += oprot->writeListEnd(); - } - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldBegin("boundary_order", ::apache::thrift::protocol::T_I32, 4); - xfer += oprot->writeI32((int32_t)this->boundary_order); - xfer += oprot->writeFieldEnd(); - - if (this->__isset.null_counts) { - xfer += oprot->writeFieldBegin("null_counts", ::apache::thrift::protocol::T_LIST, 5); - { - xfer += oprot->writeListBegin(::apache::thrift::protocol::T_I64, static_cast(this->null_counts.size())); - std::vector ::const_iterator _iter176; - for (_iter176 = this->null_counts.begin(); _iter176 != this->null_counts.end(); ++_iter176) - { - xfer += oprot->writeI64((*_iter176)); - } - xfer += oprot->writeListEnd(); - } - xfer += oprot->writeFieldEnd(); - } - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(ColumnIndex &a, ColumnIndex &b) { - using ::std::swap; - swap(a.null_pages, b.null_pages); - swap(a.min_values, b.min_values); - swap(a.max_values, b.max_values); - swap(a.boundary_order, b.boundary_order); - swap(a.null_counts, b.null_counts); - swap(a.__isset, b.__isset); -} - -ColumnIndex::ColumnIndex(const ColumnIndex& other177) { - null_pages = other177.null_pages; - min_values = other177.min_values; - max_values = other177.max_values; - boundary_order = other177.boundary_order; - null_counts = other177.null_counts; - __isset = other177.__isset; -} -ColumnIndex& ColumnIndex::operator=(const ColumnIndex& other178) { - null_pages = other178.null_pages; - min_values = other178.min_values; - max_values = other178.max_values; - boundary_order = other178.boundary_order; - null_counts = other178.null_counts; - __isset = other178.__isset; - return *this; -} -void ColumnIndex::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "ColumnIndex("; - out << "null_pages=" << to_string(null_pages); - out << ", " << "min_values=" << to_string(min_values); - out << ", " << "max_values=" << to_string(max_values); - out << ", " << "boundary_order=" << to_string(boundary_order); - out << ", " << "null_counts="; (__isset.null_counts ? (out << to_string(null_counts)) : (out << "")); - out << ")"; -} - - -AesGcmV1::~AesGcmV1() throw() { -} - - -void AesGcmV1::__set_aad_prefix(const std::string& val) { - this->aad_prefix = val; -__isset.aad_prefix = true; -} - -void AesGcmV1::__set_aad_file_unique(const std::string& val) { - this->aad_file_unique = val; -__isset.aad_file_unique = true; -} - -void AesGcmV1::__set_supply_aad_prefix(const bool val) { - this->supply_aad_prefix = val; -__isset.supply_aad_prefix = true; -} -std::ostream& operator<<(std::ostream& out, const AesGcmV1& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t AesGcmV1::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - switch (fid) - { - case 1: - if (ftype == ::apache::thrift::protocol::T_STRING) { - xfer += iprot->readBinary(this->aad_prefix); - this->__isset.aad_prefix = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 2: - if (ftype == ::apache::thrift::protocol::T_STRING) { - xfer += iprot->readBinary(this->aad_file_unique); - this->__isset.aad_file_unique = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 3: - if (ftype == ::apache::thrift::protocol::T_BOOL) { - xfer += iprot->readBool(this->supply_aad_prefix); - this->__isset.supply_aad_prefix = true; - } else { - xfer += iprot->skip(ftype); - } - break; - default: - xfer += iprot->skip(ftype); - break; - } - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - return xfer; -} - -uint32_t AesGcmV1::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("AesGcmV1"); - - if (this->__isset.aad_prefix) { - xfer += oprot->writeFieldBegin("aad_prefix", ::apache::thrift::protocol::T_STRING, 1); - xfer += oprot->writeBinary(this->aad_prefix); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.aad_file_unique) { - xfer += oprot->writeFieldBegin("aad_file_unique", ::apache::thrift::protocol::T_STRING, 2); - xfer += oprot->writeBinary(this->aad_file_unique); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.supply_aad_prefix) { - xfer += oprot->writeFieldBegin("supply_aad_prefix", ::apache::thrift::protocol::T_BOOL, 3); - xfer += oprot->writeBool(this->supply_aad_prefix); - xfer += oprot->writeFieldEnd(); - } - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(AesGcmV1 &a, AesGcmV1 &b) { - using ::std::swap; - swap(a.aad_prefix, b.aad_prefix); - swap(a.aad_file_unique, b.aad_file_unique); - swap(a.supply_aad_prefix, b.supply_aad_prefix); - swap(a.__isset, b.__isset); -} - -AesGcmV1::AesGcmV1(const AesGcmV1& other179) { - aad_prefix = other179.aad_prefix; - aad_file_unique = other179.aad_file_unique; - supply_aad_prefix = other179.supply_aad_prefix; - __isset = other179.__isset; -} -AesGcmV1& AesGcmV1::operator=(const AesGcmV1& other180) { - aad_prefix = other180.aad_prefix; - aad_file_unique = other180.aad_file_unique; - supply_aad_prefix = other180.supply_aad_prefix; - __isset = other180.__isset; - return *this; -} -void AesGcmV1::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "AesGcmV1("; - out << "aad_prefix="; (__isset.aad_prefix ? (out << to_string(aad_prefix)) : (out << "")); - out << ", " << "aad_file_unique="; (__isset.aad_file_unique ? (out << to_string(aad_file_unique)) : (out << "")); - out << ", " << "supply_aad_prefix="; (__isset.supply_aad_prefix ? (out << to_string(supply_aad_prefix)) : (out << "")); - out << ")"; -} - - -AesGcmCtrV1::~AesGcmCtrV1() throw() { -} - - -void AesGcmCtrV1::__set_aad_prefix(const std::string& val) { - this->aad_prefix = val; -__isset.aad_prefix = true; -} - -void AesGcmCtrV1::__set_aad_file_unique(const std::string& val) { - this->aad_file_unique = val; -__isset.aad_file_unique = true; -} - -void AesGcmCtrV1::__set_supply_aad_prefix(const bool val) { - this->supply_aad_prefix = val; -__isset.supply_aad_prefix = true; -} -std::ostream& operator<<(std::ostream& out, const AesGcmCtrV1& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t AesGcmCtrV1::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - switch (fid) - { - case 1: - if (ftype == ::apache::thrift::protocol::T_STRING) { - xfer += iprot->readBinary(this->aad_prefix); - this->__isset.aad_prefix = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 2: - if (ftype == ::apache::thrift::protocol::T_STRING) { - xfer += iprot->readBinary(this->aad_file_unique); - this->__isset.aad_file_unique = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 3: - if (ftype == ::apache::thrift::protocol::T_BOOL) { - xfer += iprot->readBool(this->supply_aad_prefix); - this->__isset.supply_aad_prefix = true; - } else { - xfer += iprot->skip(ftype); - } - break; - default: - xfer += iprot->skip(ftype); - break; - } - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - return xfer; -} - -uint32_t AesGcmCtrV1::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("AesGcmCtrV1"); - - if (this->__isset.aad_prefix) { - xfer += oprot->writeFieldBegin("aad_prefix", ::apache::thrift::protocol::T_STRING, 1); - xfer += oprot->writeBinary(this->aad_prefix); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.aad_file_unique) { - xfer += oprot->writeFieldBegin("aad_file_unique", ::apache::thrift::protocol::T_STRING, 2); - xfer += oprot->writeBinary(this->aad_file_unique); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.supply_aad_prefix) { - xfer += oprot->writeFieldBegin("supply_aad_prefix", ::apache::thrift::protocol::T_BOOL, 3); - xfer += oprot->writeBool(this->supply_aad_prefix); - xfer += oprot->writeFieldEnd(); - } - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(AesGcmCtrV1 &a, AesGcmCtrV1 &b) { - using ::std::swap; - swap(a.aad_prefix, b.aad_prefix); - swap(a.aad_file_unique, b.aad_file_unique); - swap(a.supply_aad_prefix, b.supply_aad_prefix); - swap(a.__isset, b.__isset); -} - -AesGcmCtrV1::AesGcmCtrV1(const AesGcmCtrV1& other181) { - aad_prefix = other181.aad_prefix; - aad_file_unique = other181.aad_file_unique; - supply_aad_prefix = other181.supply_aad_prefix; - __isset = other181.__isset; -} -AesGcmCtrV1& AesGcmCtrV1::operator=(const AesGcmCtrV1& other182) { - aad_prefix = other182.aad_prefix; - aad_file_unique = other182.aad_file_unique; - supply_aad_prefix = other182.supply_aad_prefix; - __isset = other182.__isset; - return *this; -} -void AesGcmCtrV1::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "AesGcmCtrV1("; - out << "aad_prefix="; (__isset.aad_prefix ? (out << to_string(aad_prefix)) : (out << "")); - out << ", " << "aad_file_unique="; (__isset.aad_file_unique ? (out << to_string(aad_file_unique)) : (out << "")); - out << ", " << "supply_aad_prefix="; (__isset.supply_aad_prefix ? (out << to_string(supply_aad_prefix)) : (out << "")); - out << ")"; -} - - -EncryptionAlgorithm::~EncryptionAlgorithm() throw() { -} - - -void EncryptionAlgorithm::__set_AES_GCM_V1(const AesGcmV1& val) { - this->AES_GCM_V1 = val; -__isset.AES_GCM_V1 = true; -} - -void EncryptionAlgorithm::__set_AES_GCM_CTR_V1(const AesGcmCtrV1& val) { - this->AES_GCM_CTR_V1 = val; -__isset.AES_GCM_CTR_V1 = true; -} -std::ostream& operator<<(std::ostream& out, const EncryptionAlgorithm& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t EncryptionAlgorithm::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - switch (fid) - { - case 1: - if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->AES_GCM_V1.read(iprot); - this->__isset.AES_GCM_V1 = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 2: - if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->AES_GCM_CTR_V1.read(iprot); - this->__isset.AES_GCM_CTR_V1 = true; - } else { - xfer += iprot->skip(ftype); - } - break; - default: - xfer += iprot->skip(ftype); - break; - } - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - return xfer; -} - -uint32_t EncryptionAlgorithm::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("EncryptionAlgorithm"); - - if (this->__isset.AES_GCM_V1) { - xfer += oprot->writeFieldBegin("AES_GCM_V1", ::apache::thrift::protocol::T_STRUCT, 1); - xfer += this->AES_GCM_V1.write(oprot); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.AES_GCM_CTR_V1) { - xfer += oprot->writeFieldBegin("AES_GCM_CTR_V1", ::apache::thrift::protocol::T_STRUCT, 2); - xfer += this->AES_GCM_CTR_V1.write(oprot); - xfer += oprot->writeFieldEnd(); - } - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(EncryptionAlgorithm &a, EncryptionAlgorithm &b) { - using ::std::swap; - swap(a.AES_GCM_V1, b.AES_GCM_V1); - swap(a.AES_GCM_CTR_V1, b.AES_GCM_CTR_V1); - swap(a.__isset, b.__isset); -} - -EncryptionAlgorithm::EncryptionAlgorithm(const EncryptionAlgorithm& other183) { - AES_GCM_V1 = other183.AES_GCM_V1; - AES_GCM_CTR_V1 = other183.AES_GCM_CTR_V1; - __isset = other183.__isset; -} -EncryptionAlgorithm& EncryptionAlgorithm::operator=(const EncryptionAlgorithm& other184) { - AES_GCM_V1 = other184.AES_GCM_V1; - AES_GCM_CTR_V1 = other184.AES_GCM_CTR_V1; - __isset = other184.__isset; - return *this; -} -void EncryptionAlgorithm::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "EncryptionAlgorithm("; - out << "AES_GCM_V1="; (__isset.AES_GCM_V1 ? (out << to_string(AES_GCM_V1)) : (out << "")); - out << ", " << "AES_GCM_CTR_V1="; (__isset.AES_GCM_CTR_V1 ? (out << to_string(AES_GCM_CTR_V1)) : (out << "")); - out << ")"; -} - - -FileMetaData::~FileMetaData() throw() { -} - - -void FileMetaData::__set_version(const int32_t val) { - this->version = val; -} - -void FileMetaData::__set_schema(const std::vector & val) { - this->schema = val; -} - -void FileMetaData::__set_num_rows(const int64_t val) { - this->num_rows = val; -} - -void FileMetaData::__set_row_groups(const std::vector & val) { - this->row_groups = val; -} - -void FileMetaData::__set_key_value_metadata(const std::vector & val) { - this->key_value_metadata = val; -__isset.key_value_metadata = true; -} - -void FileMetaData::__set_created_by(const std::string& val) { - this->created_by = val; -__isset.created_by = true; -} - -void FileMetaData::__set_column_orders(const std::vector & val) { - this->column_orders = val; -__isset.column_orders = true; -} - -void FileMetaData::__set_encryption_algorithm(const EncryptionAlgorithm& val) { - this->encryption_algorithm = val; -__isset.encryption_algorithm = true; -} - -void FileMetaData::__set_footer_signing_key_metadata(const std::string& val) { - this->footer_signing_key_metadata = val; -__isset.footer_signing_key_metadata = true; -} -std::ostream& operator<<(std::ostream& out, const FileMetaData& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t FileMetaData::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - bool isset_version = false; - bool isset_schema = false; - bool isset_num_rows = false; - bool isset_row_groups = false; - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - switch (fid) - { - case 1: - if (ftype == ::apache::thrift::protocol::T_I32) { - xfer += iprot->readI32(this->version); - isset_version = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 2: - if (ftype == ::apache::thrift::protocol::T_LIST) { - { - this->schema.clear(); - uint32_t _size185; - ::apache::thrift::protocol::TType _etype188; - xfer += iprot->readListBegin(_etype188, _size185); - this->schema.resize(_size185); - uint32_t _i189; - for (_i189 = 0; _i189 < _size185; ++_i189) - { - xfer += this->schema[_i189].read(iprot); - } - xfer += iprot->readListEnd(); - } - isset_schema = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 3: - if (ftype == ::apache::thrift::protocol::T_I64) { - xfer += iprot->readI64(this->num_rows); - isset_num_rows = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 4: - if (ftype == ::apache::thrift::protocol::T_LIST) { - { - this->row_groups.clear(); - uint32_t _size190; - ::apache::thrift::protocol::TType _etype193; - xfer += iprot->readListBegin(_etype193, _size190); - this->row_groups.resize(_size190); - uint32_t _i194; - for (_i194 = 0; _i194 < _size190; ++_i194) - { - xfer += this->row_groups[_i194].read(iprot); - } - xfer += iprot->readListEnd(); - } - isset_row_groups = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 5: - if (ftype == ::apache::thrift::protocol::T_LIST) { - { - this->key_value_metadata.clear(); - uint32_t _size195; - ::apache::thrift::protocol::TType _etype198; - xfer += iprot->readListBegin(_etype198, _size195); - this->key_value_metadata.resize(_size195); - uint32_t _i199; - for (_i199 = 0; _i199 < _size195; ++_i199) - { - xfer += this->key_value_metadata[_i199].read(iprot); - } - xfer += iprot->readListEnd(); - } - this->__isset.key_value_metadata = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 6: - if (ftype == ::apache::thrift::protocol::T_STRING) { - xfer += iprot->readString(this->created_by); - this->__isset.created_by = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 7: - if (ftype == ::apache::thrift::protocol::T_LIST) { - { - this->column_orders.clear(); - uint32_t _size200; - ::apache::thrift::protocol::TType _etype203; - xfer += iprot->readListBegin(_etype203, _size200); - this->column_orders.resize(_size200); - uint32_t _i204; - for (_i204 = 0; _i204 < _size200; ++_i204) - { - xfer += this->column_orders[_i204].read(iprot); - } - xfer += iprot->readListEnd(); - } - this->__isset.column_orders = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 8: - if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->encryption_algorithm.read(iprot); - this->__isset.encryption_algorithm = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 9: - if (ftype == ::apache::thrift::protocol::T_STRING) { - xfer += iprot->readBinary(this->footer_signing_key_metadata); - this->__isset.footer_signing_key_metadata = true; - } else { - xfer += iprot->skip(ftype); - } - break; - default: - xfer += iprot->skip(ftype); - break; - } - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - if (!isset_version) - throw TProtocolException(TProtocolException::INVALID_DATA); - if (!isset_schema) - throw TProtocolException(TProtocolException::INVALID_DATA); - if (!isset_num_rows) - throw TProtocolException(TProtocolException::INVALID_DATA); - if (!isset_row_groups) - throw TProtocolException(TProtocolException::INVALID_DATA); - return xfer; -} - -uint32_t FileMetaData::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("FileMetaData"); - - xfer += oprot->writeFieldBegin("version", ::apache::thrift::protocol::T_I32, 1); - xfer += oprot->writeI32(this->version); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldBegin("schema", ::apache::thrift::protocol::T_LIST, 2); - { - xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, static_cast(this->schema.size())); - std::vector ::const_iterator _iter205; - for (_iter205 = this->schema.begin(); _iter205 != this->schema.end(); ++_iter205) - { - xfer += (*_iter205).write(oprot); - } - xfer += oprot->writeListEnd(); - } - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldBegin("num_rows", ::apache::thrift::protocol::T_I64, 3); - xfer += oprot->writeI64(this->num_rows); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldBegin("row_groups", ::apache::thrift::protocol::T_LIST, 4); - { - xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, static_cast(this->row_groups.size())); - std::vector ::const_iterator _iter206; - for (_iter206 = this->row_groups.begin(); _iter206 != this->row_groups.end(); ++_iter206) - { - xfer += (*_iter206).write(oprot); - } - xfer += oprot->writeListEnd(); - } - xfer += oprot->writeFieldEnd(); - - if (this->__isset.key_value_metadata) { - xfer += oprot->writeFieldBegin("key_value_metadata", ::apache::thrift::protocol::T_LIST, 5); - { - xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, static_cast(this->key_value_metadata.size())); - std::vector ::const_iterator _iter207; - for (_iter207 = this->key_value_metadata.begin(); _iter207 != this->key_value_metadata.end(); ++_iter207) - { - xfer += (*_iter207).write(oprot); - } - xfer += oprot->writeListEnd(); - } - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.created_by) { - xfer += oprot->writeFieldBegin("created_by", ::apache::thrift::protocol::T_STRING, 6); - xfer += oprot->writeString(this->created_by); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.column_orders) { - xfer += oprot->writeFieldBegin("column_orders", ::apache::thrift::protocol::T_LIST, 7); - { - xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, static_cast(this->column_orders.size())); - std::vector ::const_iterator _iter208; - for (_iter208 = this->column_orders.begin(); _iter208 != this->column_orders.end(); ++_iter208) - { - xfer += (*_iter208).write(oprot); - } - xfer += oprot->writeListEnd(); - } - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.encryption_algorithm) { - xfer += oprot->writeFieldBegin("encryption_algorithm", ::apache::thrift::protocol::T_STRUCT, 8); - xfer += this->encryption_algorithm.write(oprot); - xfer += oprot->writeFieldEnd(); - } - if (this->__isset.footer_signing_key_metadata) { - xfer += oprot->writeFieldBegin("footer_signing_key_metadata", ::apache::thrift::protocol::T_STRING, 9); - xfer += oprot->writeBinary(this->footer_signing_key_metadata); - xfer += oprot->writeFieldEnd(); - } - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(FileMetaData &a, FileMetaData &b) { - using ::std::swap; - swap(a.version, b.version); - swap(a.schema, b.schema); - swap(a.num_rows, b.num_rows); - swap(a.row_groups, b.row_groups); - swap(a.key_value_metadata, b.key_value_metadata); - swap(a.created_by, b.created_by); - swap(a.column_orders, b.column_orders); - swap(a.encryption_algorithm, b.encryption_algorithm); - swap(a.footer_signing_key_metadata, b.footer_signing_key_metadata); - swap(a.__isset, b.__isset); -} - -FileMetaData::FileMetaData(const FileMetaData& other209) { - version = other209.version; - schema = other209.schema; - num_rows = other209.num_rows; - row_groups = other209.row_groups; - key_value_metadata = other209.key_value_metadata; - created_by = other209.created_by; - column_orders = other209.column_orders; - encryption_algorithm = other209.encryption_algorithm; - footer_signing_key_metadata = other209.footer_signing_key_metadata; - __isset = other209.__isset; -} -FileMetaData& FileMetaData::operator=(const FileMetaData& other210) { - version = other210.version; - schema = other210.schema; - num_rows = other210.num_rows; - row_groups = other210.row_groups; - key_value_metadata = other210.key_value_metadata; - created_by = other210.created_by; - column_orders = other210.column_orders; - encryption_algorithm = other210.encryption_algorithm; - footer_signing_key_metadata = other210.footer_signing_key_metadata; - __isset = other210.__isset; - return *this; -} -void FileMetaData::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "FileMetaData("; - out << "version=" << to_string(version); - out << ", " << "schema=" << to_string(schema); - out << ", " << "num_rows=" << to_string(num_rows); - out << ", " << "row_groups=" << to_string(row_groups); - out << ", " << "key_value_metadata="; (__isset.key_value_metadata ? (out << to_string(key_value_metadata)) : (out << "")); - out << ", " << "created_by="; (__isset.created_by ? (out << to_string(created_by)) : (out << "")); - out << ", " << "column_orders="; (__isset.column_orders ? (out << to_string(column_orders)) : (out << "")); - out << ", " << "encryption_algorithm="; (__isset.encryption_algorithm ? (out << to_string(encryption_algorithm)) : (out << "")); - out << ", " << "footer_signing_key_metadata="; (__isset.footer_signing_key_metadata ? (out << to_string(footer_signing_key_metadata)) : (out << "")); - out << ")"; -} - - -FileCryptoMetaData::~FileCryptoMetaData() throw() { -} - - -void FileCryptoMetaData::__set_encryption_algorithm(const EncryptionAlgorithm& val) { - this->encryption_algorithm = val; -} - -void FileCryptoMetaData::__set_key_metadata(const std::string& val) { - this->key_metadata = val; -__isset.key_metadata = true; -} -std::ostream& operator<<(std::ostream& out, const FileCryptoMetaData& obj) -{ - obj.printTo(out); - return out; -} - - -uint32_t FileCryptoMetaData::read(::apache::thrift::protocol::TProtocol* iprot) { - - ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); - uint32_t xfer = 0; - std::string fname; - ::apache::thrift::protocol::TType ftype; - int16_t fid; - - xfer += iprot->readStructBegin(fname); - - using ::apache::thrift::protocol::TProtocolException; - - bool isset_encryption_algorithm = false; - - while (true) - { - xfer += iprot->readFieldBegin(fname, ftype, fid); - if (ftype == ::apache::thrift::protocol::T_STOP) { - break; - } - switch (fid) - { - case 1: - if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->encryption_algorithm.read(iprot); - isset_encryption_algorithm = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 2: - if (ftype == ::apache::thrift::protocol::T_STRING) { - xfer += iprot->readBinary(this->key_metadata); - this->__isset.key_metadata = true; - } else { - xfer += iprot->skip(ftype); - } - break; - default: - xfer += iprot->skip(ftype); - break; - } - xfer += iprot->readFieldEnd(); - } - - xfer += iprot->readStructEnd(); - - if (!isset_encryption_algorithm) - throw TProtocolException(TProtocolException::INVALID_DATA); - return xfer; -} - -uint32_t FileCryptoMetaData::write(::apache::thrift::protocol::TProtocol* oprot) const { - uint32_t xfer = 0; - ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("FileCryptoMetaData"); - - xfer += oprot->writeFieldBegin("encryption_algorithm", ::apache::thrift::protocol::T_STRUCT, 1); - xfer += this->encryption_algorithm.write(oprot); - xfer += oprot->writeFieldEnd(); - - if (this->__isset.key_metadata) { - xfer += oprot->writeFieldBegin("key_metadata", ::apache::thrift::protocol::T_STRING, 2); - xfer += oprot->writeBinary(this->key_metadata); - xfer += oprot->writeFieldEnd(); - } - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(FileCryptoMetaData &a, FileCryptoMetaData &b) { - using ::std::swap; - swap(a.encryption_algorithm, b.encryption_algorithm); - swap(a.key_metadata, b.key_metadata); - swap(a.__isset, b.__isset); -} - -FileCryptoMetaData::FileCryptoMetaData(const FileCryptoMetaData& other211) { - encryption_algorithm = other211.encryption_algorithm; - key_metadata = other211.key_metadata; - __isset = other211.__isset; -} -FileCryptoMetaData& FileCryptoMetaData::operator=(const FileCryptoMetaData& other212) { - encryption_algorithm = other212.encryption_algorithm; - key_metadata = other212.key_metadata; - __isset = other212.__isset; - return *this; -} -void FileCryptoMetaData::printTo(std::ostream& out) const { - using ::apache::thrift::to_string; - out << "FileCryptoMetaData("; - out << "encryption_algorithm=" << to_string(encryption_algorithm); - out << ", " << "key_metadata="; (__isset.key_metadata ? (out << to_string(key_metadata)) : (out << "")); - out << ")"; -} - -}} // namespace diff --git a/third_party/parquet/parquet_types.h b/third_party/parquet/parquet_types.h deleted file mode 100644 index 78ade1312..000000000 --- a/third_party/parquet/parquet_types.h +++ /dev/null @@ -1,2901 +0,0 @@ -/** - * Autogenerated by Thrift Compiler (0.12.0) - * - * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING - * @generated - */ -#ifndef parquet_TYPES_H -#define parquet_TYPES_H - -#include - -#include -#include -#include -#include -#include - -#include - -#include "parquet/windows_compatibility.h" - -namespace parquet { namespace format { - -struct Type { - enum type { - BOOLEAN = 0, - INT32 = 1, - INT64 = 2, - INT96 = 3, - FLOAT = 4, - DOUBLE = 5, - BYTE_ARRAY = 6, - FIXED_LEN_BYTE_ARRAY = 7 - }; -}; - -extern const std::map _Type_VALUES_TO_NAMES; - -std::ostream& operator<<(std::ostream& out, const Type::type& val); - -struct ConvertedType { - enum type { - UTF8 = 0, - MAP = 1, - MAP_KEY_VALUE = 2, - LIST = 3, - ENUM = 4, - DECIMAL = 5, - DATE = 6, - TIME_MILLIS = 7, - TIME_MICROS = 8, - TIMESTAMP_MILLIS = 9, - TIMESTAMP_MICROS = 10, - UINT_8 = 11, - UINT_16 = 12, - UINT_32 = 13, - UINT_64 = 14, - INT_8 = 15, - INT_16 = 16, - INT_32 = 17, - INT_64 = 18, - JSON = 19, - BSON = 20, - INTERVAL = 21 - }; -}; - -extern const std::map _ConvertedType_VALUES_TO_NAMES; - -std::ostream& operator<<(std::ostream& out, const ConvertedType::type& val); - -struct FieldRepetitionType { - enum type { - REQUIRED = 0, - OPTIONAL = 1, - REPEATED = 2 - }; -}; - -extern const std::map _FieldRepetitionType_VALUES_TO_NAMES; - -std::ostream& operator<<(std::ostream& out, const FieldRepetitionType::type& val); - -struct Encoding { - enum type { - PLAIN = 0, - PLAIN_DICTIONARY = 2, - RLE = 3, - BIT_PACKED = 4, - DELTA_BINARY_PACKED = 5, - DELTA_LENGTH_BYTE_ARRAY = 6, - DELTA_BYTE_ARRAY = 7, - RLE_DICTIONARY = 8, - BYTE_STREAM_SPLIT = 9 - }; -}; - -extern const std::map _Encoding_VALUES_TO_NAMES; - -std::ostream& operator<<(std::ostream& out, const Encoding::type& val); - -struct CompressionCodec { - enum type { - UNCOMPRESSED = 0, - SNAPPY = 1, - GZIP = 2, - LZO = 3, - BROTLI = 4, - LZ4 = 5, - ZSTD = 6 - }; -}; - -extern const std::map _CompressionCodec_VALUES_TO_NAMES; - -std::ostream& operator<<(std::ostream& out, const CompressionCodec::type& val); - -struct PageType { - enum type { - DATA_PAGE = 0, - INDEX_PAGE = 1, - DICTIONARY_PAGE = 2, - DATA_PAGE_V2 = 3 - }; -}; - -extern const std::map _PageType_VALUES_TO_NAMES; - -std::ostream& operator<<(std::ostream& out, const PageType::type& val); - -struct BoundaryOrder { - enum type { - UNORDERED = 0, - ASCENDING = 1, - DESCENDING = 2 - }; -}; - -extern const std::map _BoundaryOrder_VALUES_TO_NAMES; - -std::ostream& operator<<(std::ostream& out, const BoundaryOrder::type& val); - -class Statistics; - -class StringType; - -class UUIDType; - -class MapType; - -class ListType; - -class EnumType; - -class DateType; - -class NullType; - -class DecimalType; - -class MilliSeconds; - -class MicroSeconds; - -class NanoSeconds; - -class TimeUnit; - -class TimestampType; - -class TimeType; - -class IntType; - -class JsonType; - -class BsonType; - -class LogicalType; - -class SchemaElement; - -class DataPageHeader; - -class IndexPageHeader; - -class DictionaryPageHeader; - -class DataPageHeaderV2; - -class SplitBlockAlgorithm; - -class BloomFilterAlgorithm; - -class XxHash; - -class BloomFilterHash; - -class Uncompressed; - -class BloomFilterCompression; - -class BloomFilterHeader; - -class PageHeader; - -class KeyValue; - -class SortingColumn; - -class PageEncodingStats; - -class ColumnMetaData; - -class EncryptionWithFooterKey; - -class EncryptionWithColumnKey; - -class ColumnCryptoMetaData; - -class ColumnChunk; - -class RowGroup; - -class TypeDefinedOrder; - -class ColumnOrder; - -class PageLocation; - -class OffsetIndex; - -class ColumnIndex; - -class AesGcmV1; - -class AesGcmCtrV1; - -class EncryptionAlgorithm; - -class FileMetaData; - -class FileCryptoMetaData; - -typedef struct _Statistics__isset { - _Statistics__isset() : max(false), min(false), null_count(false), distinct_count(false), max_value(false), min_value(false) {} - bool max :1; - bool min :1; - bool null_count :1; - bool distinct_count :1; - bool max_value :1; - bool min_value :1; -} _Statistics__isset; - -class Statistics : public virtual ::apache::thrift::TBase { - public: - - Statistics(const Statistics&); - Statistics& operator=(const Statistics&); - Statistics() : max(), min(), null_count(0), distinct_count(0), max_value(), min_value() { - } - - virtual ~Statistics() throw(); - std::string max; - std::string min; - int64_t null_count; - int64_t distinct_count; - std::string max_value; - std::string min_value; - - _Statistics__isset __isset; - - void __set_max(const std::string& val); - - void __set_min(const std::string& val); - - void __set_null_count(const int64_t val); - - void __set_distinct_count(const int64_t val); - - void __set_max_value(const std::string& val); - - void __set_min_value(const std::string& val); - - bool operator == (const Statistics & rhs) const - { - if (__isset.max != rhs.__isset.max) - return false; - else if (__isset.max && !(max == rhs.max)) - return false; - if (__isset.min != rhs.__isset.min) - return false; - else if (__isset.min && !(min == rhs.min)) - return false; - if (__isset.null_count != rhs.__isset.null_count) - return false; - else if (__isset.null_count && !(null_count == rhs.null_count)) - return false; - if (__isset.distinct_count != rhs.__isset.distinct_count) - return false; - else if (__isset.distinct_count && !(distinct_count == rhs.distinct_count)) - return false; - if (__isset.max_value != rhs.__isset.max_value) - return false; - else if (__isset.max_value && !(max_value == rhs.max_value)) - return false; - if (__isset.min_value != rhs.__isset.min_value) - return false; - else if (__isset.min_value && !(min_value == rhs.min_value)) - return false; - return true; - } - bool operator != (const Statistics &rhs) const { - return !(*this == rhs); - } - - bool operator < (const Statistics & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(Statistics &a, Statistics &b); - -std::ostream& operator<<(std::ostream& out, const Statistics& obj); - - -class StringType : public virtual ::apache::thrift::TBase { - public: - - StringType(const StringType&); - StringType& operator=(const StringType&); - StringType() { - } - - virtual ~StringType() throw(); - - bool operator == (const StringType & /* rhs */) const - { - return true; - } - bool operator != (const StringType &rhs) const { - return !(*this == rhs); - } - - bool operator < (const StringType & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(StringType &a, StringType &b); - -std::ostream& operator<<(std::ostream& out, const StringType& obj); - - -class UUIDType : public virtual ::apache::thrift::TBase { - public: - - UUIDType(const UUIDType&); - UUIDType& operator=(const UUIDType&); - UUIDType() { - } - - virtual ~UUIDType() throw(); - - bool operator == (const UUIDType & /* rhs */) const - { - return true; - } - bool operator != (const UUIDType &rhs) const { - return !(*this == rhs); - } - - bool operator < (const UUIDType & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(UUIDType &a, UUIDType &b); - -std::ostream& operator<<(std::ostream& out, const UUIDType& obj); - - -class MapType : public virtual ::apache::thrift::TBase { - public: - - MapType(const MapType&); - MapType& operator=(const MapType&); - MapType() { - } - - virtual ~MapType() throw(); - - bool operator == (const MapType & /* rhs */) const - { - return true; - } - bool operator != (const MapType &rhs) const { - return !(*this == rhs); - } - - bool operator < (const MapType & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(MapType &a, MapType &b); - -std::ostream& operator<<(std::ostream& out, const MapType& obj); - - -class ListType : public virtual ::apache::thrift::TBase { - public: - - ListType(const ListType&); - ListType& operator=(const ListType&); - ListType() { - } - - virtual ~ListType() throw(); - - bool operator == (const ListType & /* rhs */) const - { - return true; - } - bool operator != (const ListType &rhs) const { - return !(*this == rhs); - } - - bool operator < (const ListType & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(ListType &a, ListType &b); - -std::ostream& operator<<(std::ostream& out, const ListType& obj); - - -class EnumType : public virtual ::apache::thrift::TBase { - public: - - EnumType(const EnumType&); - EnumType& operator=(const EnumType&); - EnumType() { - } - - virtual ~EnumType() throw(); - - bool operator == (const EnumType & /* rhs */) const - { - return true; - } - bool operator != (const EnumType &rhs) const { - return !(*this == rhs); - } - - bool operator < (const EnumType & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(EnumType &a, EnumType &b); - -std::ostream& operator<<(std::ostream& out, const EnumType& obj); - - -class DateType : public virtual ::apache::thrift::TBase { - public: - - DateType(const DateType&); - DateType& operator=(const DateType&); - DateType() { - } - - virtual ~DateType() throw(); - - bool operator == (const DateType & /* rhs */) const - { - return true; - } - bool operator != (const DateType &rhs) const { - return !(*this == rhs); - } - - bool operator < (const DateType & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(DateType &a, DateType &b); - -std::ostream& operator<<(std::ostream& out, const DateType& obj); - - -class NullType : public virtual ::apache::thrift::TBase { - public: - - NullType(const NullType&); - NullType& operator=(const NullType&); - NullType() { - } - - virtual ~NullType() throw(); - - bool operator == (const NullType & /* rhs */) const - { - return true; - } - bool operator != (const NullType &rhs) const { - return !(*this == rhs); - } - - bool operator < (const NullType & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(NullType &a, NullType &b); - -std::ostream& operator<<(std::ostream& out, const NullType& obj); - - -class DecimalType : public virtual ::apache::thrift::TBase { - public: - - DecimalType(const DecimalType&); - DecimalType& operator=(const DecimalType&); - DecimalType() : scale(0), precision(0) { - } - - virtual ~DecimalType() throw(); - int32_t scale; - int32_t precision; - - void __set_scale(const int32_t val); - - void __set_precision(const int32_t val); - - bool operator == (const DecimalType & rhs) const - { - if (!(scale == rhs.scale)) - return false; - if (!(precision == rhs.precision)) - return false; - return true; - } - bool operator != (const DecimalType &rhs) const { - return !(*this == rhs); - } - - bool operator < (const DecimalType & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(DecimalType &a, DecimalType &b); - -std::ostream& operator<<(std::ostream& out, const DecimalType& obj); - - -class MilliSeconds : public virtual ::apache::thrift::TBase { - public: - - MilliSeconds(const MilliSeconds&); - MilliSeconds& operator=(const MilliSeconds&); - MilliSeconds() { - } - - virtual ~MilliSeconds() throw(); - - bool operator == (const MilliSeconds & /* rhs */) const - { - return true; - } - bool operator != (const MilliSeconds &rhs) const { - return !(*this == rhs); - } - - bool operator < (const MilliSeconds & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(MilliSeconds &a, MilliSeconds &b); - -std::ostream& operator<<(std::ostream& out, const MilliSeconds& obj); - - -class MicroSeconds : public virtual ::apache::thrift::TBase { - public: - - MicroSeconds(const MicroSeconds&); - MicroSeconds& operator=(const MicroSeconds&); - MicroSeconds() { - } - - virtual ~MicroSeconds() throw(); - - bool operator == (const MicroSeconds & /* rhs */) const - { - return true; - } - bool operator != (const MicroSeconds &rhs) const { - return !(*this == rhs); - } - - bool operator < (const MicroSeconds & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(MicroSeconds &a, MicroSeconds &b); - -std::ostream& operator<<(std::ostream& out, const MicroSeconds& obj); - - -class NanoSeconds : public virtual ::apache::thrift::TBase { - public: - - NanoSeconds(const NanoSeconds&); - NanoSeconds& operator=(const NanoSeconds&); - NanoSeconds() { - } - - virtual ~NanoSeconds() throw(); - - bool operator == (const NanoSeconds & /* rhs */) const - { - return true; - } - bool operator != (const NanoSeconds &rhs) const { - return !(*this == rhs); - } - - bool operator < (const NanoSeconds & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(NanoSeconds &a, NanoSeconds &b); - -std::ostream& operator<<(std::ostream& out, const NanoSeconds& obj); - -typedef struct _TimeUnit__isset { - _TimeUnit__isset() : MILLIS(false), MICROS(false), NANOS(false) {} - bool MILLIS :1; - bool MICROS :1; - bool NANOS :1; -} _TimeUnit__isset; - -class TimeUnit : public virtual ::apache::thrift::TBase { - public: - - TimeUnit(const TimeUnit&); - TimeUnit& operator=(const TimeUnit&); - TimeUnit() { - } - - virtual ~TimeUnit() throw(); - MilliSeconds MILLIS; - MicroSeconds MICROS; - NanoSeconds NANOS; - - _TimeUnit__isset __isset; - - void __set_MILLIS(const MilliSeconds& val); - - void __set_MICROS(const MicroSeconds& val); - - void __set_NANOS(const NanoSeconds& val); - - bool operator == (const TimeUnit & rhs) const - { - if (__isset.MILLIS != rhs.__isset.MILLIS) - return false; - else if (__isset.MILLIS && !(MILLIS == rhs.MILLIS)) - return false; - if (__isset.MICROS != rhs.__isset.MICROS) - return false; - else if (__isset.MICROS && !(MICROS == rhs.MICROS)) - return false; - if (__isset.NANOS != rhs.__isset.NANOS) - return false; - else if (__isset.NANOS && !(NANOS == rhs.NANOS)) - return false; - return true; - } - bool operator != (const TimeUnit &rhs) const { - return !(*this == rhs); - } - - bool operator < (const TimeUnit & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(TimeUnit &a, TimeUnit &b); - -std::ostream& operator<<(std::ostream& out, const TimeUnit& obj); - - -class TimestampType : public virtual ::apache::thrift::TBase { - public: - - TimestampType(const TimestampType&); - TimestampType& operator=(const TimestampType&); - TimestampType() : isAdjustedToUTC(0) { - } - - virtual ~TimestampType() throw(); - bool isAdjustedToUTC; - TimeUnit unit; - - void __set_isAdjustedToUTC(const bool val); - - void __set_unit(const TimeUnit& val); - - bool operator == (const TimestampType & rhs) const - { - if (!(isAdjustedToUTC == rhs.isAdjustedToUTC)) - return false; - if (!(unit == rhs.unit)) - return false; - return true; - } - bool operator != (const TimestampType &rhs) const { - return !(*this == rhs); - } - - bool operator < (const TimestampType & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(TimestampType &a, TimestampType &b); - -std::ostream& operator<<(std::ostream& out, const TimestampType& obj); - - -class TimeType : public virtual ::apache::thrift::TBase { - public: - - TimeType(const TimeType&); - TimeType& operator=(const TimeType&); - TimeType() : isAdjustedToUTC(0) { - } - - virtual ~TimeType() throw(); - bool isAdjustedToUTC; - TimeUnit unit; - - void __set_isAdjustedToUTC(const bool val); - - void __set_unit(const TimeUnit& val); - - bool operator == (const TimeType & rhs) const - { - if (!(isAdjustedToUTC == rhs.isAdjustedToUTC)) - return false; - if (!(unit == rhs.unit)) - return false; - return true; - } - bool operator != (const TimeType &rhs) const { - return !(*this == rhs); - } - - bool operator < (const TimeType & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(TimeType &a, TimeType &b); - -std::ostream& operator<<(std::ostream& out, const TimeType& obj); - - -class IntType : public virtual ::apache::thrift::TBase { - public: - - IntType(const IntType&); - IntType& operator=(const IntType&); - IntType() : bitWidth(0), isSigned(0) { - } - - virtual ~IntType() throw(); - int8_t bitWidth; - bool isSigned; - - void __set_bitWidth(const int8_t val); - - void __set_isSigned(const bool val); - - bool operator == (const IntType & rhs) const - { - if (!(bitWidth == rhs.bitWidth)) - return false; - if (!(isSigned == rhs.isSigned)) - return false; - return true; - } - bool operator != (const IntType &rhs) const { - return !(*this == rhs); - } - - bool operator < (const IntType & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(IntType &a, IntType &b); - -std::ostream& operator<<(std::ostream& out, const IntType& obj); - - -class JsonType : public virtual ::apache::thrift::TBase { - public: - - JsonType(const JsonType&); - JsonType& operator=(const JsonType&); - JsonType() { - } - - virtual ~JsonType() throw(); - - bool operator == (const JsonType & /* rhs */) const - { - return true; - } - bool operator != (const JsonType &rhs) const { - return !(*this == rhs); - } - - bool operator < (const JsonType & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(JsonType &a, JsonType &b); - -std::ostream& operator<<(std::ostream& out, const JsonType& obj); - - -class BsonType : public virtual ::apache::thrift::TBase { - public: - - BsonType(const BsonType&); - BsonType& operator=(const BsonType&); - BsonType() { - } - - virtual ~BsonType() throw(); - - bool operator == (const BsonType & /* rhs */) const - { - return true; - } - bool operator != (const BsonType &rhs) const { - return !(*this == rhs); - } - - bool operator < (const BsonType & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(BsonType &a, BsonType &b); - -std::ostream& operator<<(std::ostream& out, const BsonType& obj); - -typedef struct _LogicalType__isset { - _LogicalType__isset() : STRING(false), MAP(false), LIST(false), ENUM(false), DECIMAL(false), DATE(false), TIME(false), TIMESTAMP(false), INTEGER(false), UNKNOWN(false), JSON(false), BSON(false), UUID(false) {} - bool STRING :1; - bool MAP :1; - bool LIST :1; - bool ENUM :1; - bool DECIMAL :1; - bool DATE :1; - bool TIME :1; - bool TIMESTAMP :1; - bool INTEGER :1; - bool UNKNOWN :1; - bool JSON :1; - bool BSON :1; - bool UUID :1; -} _LogicalType__isset; - -class LogicalType : public virtual ::apache::thrift::TBase { - public: - - LogicalType(const LogicalType&); - LogicalType& operator=(const LogicalType&); - LogicalType() { - } - - virtual ~LogicalType() throw(); - StringType STRING; - MapType MAP; - ListType LIST; - EnumType ENUM; - DecimalType DECIMAL; - DateType DATE; - TimeType TIME; - TimestampType TIMESTAMP; - IntType INTEGER; - NullType UNKNOWN; - JsonType JSON; - BsonType BSON; - UUIDType UUID; - - _LogicalType__isset __isset; - - void __set_STRING(const StringType& val); - - void __set_MAP(const MapType& val); - - void __set_LIST(const ListType& val); - - void __set_ENUM(const EnumType& val); - - void __set_DECIMAL(const DecimalType& val); - - void __set_DATE(const DateType& val); - - void __set_TIME(const TimeType& val); - - void __set_TIMESTAMP(const TimestampType& val); - - void __set_INTEGER(const IntType& val); - - void __set_UNKNOWN(const NullType& val); - - void __set_JSON(const JsonType& val); - - void __set_BSON(const BsonType& val); - - void __set_UUID(const UUIDType& val); - - bool operator == (const LogicalType & rhs) const - { - if (__isset.STRING != rhs.__isset.STRING) - return false; - else if (__isset.STRING && !(STRING == rhs.STRING)) - return false; - if (__isset.MAP != rhs.__isset.MAP) - return false; - else if (__isset.MAP && !(MAP == rhs.MAP)) - return false; - if (__isset.LIST != rhs.__isset.LIST) - return false; - else if (__isset.LIST && !(LIST == rhs.LIST)) - return false; - if (__isset.ENUM != rhs.__isset.ENUM) - return false; - else if (__isset.ENUM && !(ENUM == rhs.ENUM)) - return false; - if (__isset.DECIMAL != rhs.__isset.DECIMAL) - return false; - else if (__isset.DECIMAL && !(DECIMAL == rhs.DECIMAL)) - return false; - if (__isset.DATE != rhs.__isset.DATE) - return false; - else if (__isset.DATE && !(DATE == rhs.DATE)) - return false; - if (__isset.TIME != rhs.__isset.TIME) - return false; - else if (__isset.TIME && !(TIME == rhs.TIME)) - return false; - if (__isset.TIMESTAMP != rhs.__isset.TIMESTAMP) - return false; - else if (__isset.TIMESTAMP && !(TIMESTAMP == rhs.TIMESTAMP)) - return false; - if (__isset.INTEGER != rhs.__isset.INTEGER) - return false; - else if (__isset.INTEGER && !(INTEGER == rhs.INTEGER)) - return false; - if (__isset.UNKNOWN != rhs.__isset.UNKNOWN) - return false; - else if (__isset.UNKNOWN && !(UNKNOWN == rhs.UNKNOWN)) - return false; - if (__isset.JSON != rhs.__isset.JSON) - return false; - else if (__isset.JSON && !(JSON == rhs.JSON)) - return false; - if (__isset.BSON != rhs.__isset.BSON) - return false; - else if (__isset.BSON && !(BSON == rhs.BSON)) - return false; - if (__isset.UUID != rhs.__isset.UUID) - return false; - else if (__isset.UUID && !(UUID == rhs.UUID)) - return false; - return true; - } - bool operator != (const LogicalType &rhs) const { - return !(*this == rhs); - } - - bool operator < (const LogicalType & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(LogicalType &a, LogicalType &b); - -std::ostream& operator<<(std::ostream& out, const LogicalType& obj); - -typedef struct _SchemaElement__isset { - _SchemaElement__isset() : type(false), type_length(false), repetition_type(false), num_children(false), converted_type(false), scale(false), precision(false), field_id(false), logicalType(false) {} - bool type :1; - bool type_length :1; - bool repetition_type :1; - bool num_children :1; - bool converted_type :1; - bool scale :1; - bool precision :1; - bool field_id :1; - bool logicalType :1; -} _SchemaElement__isset; - -class SchemaElement : public virtual ::apache::thrift::TBase { - public: - - SchemaElement(const SchemaElement&); - SchemaElement& operator=(const SchemaElement&); - SchemaElement() : type((Type::type)0), type_length(0), repetition_type((FieldRepetitionType::type)0), name(), num_children(0), converted_type((ConvertedType::type)0), scale(0), precision(0), field_id(0) { - } - - virtual ~SchemaElement() throw(); - Type::type type; - int32_t type_length; - FieldRepetitionType::type repetition_type; - std::string name; - int32_t num_children; - ConvertedType::type converted_type; - int32_t scale; - int32_t precision; - int32_t field_id; - LogicalType logicalType; - - _SchemaElement__isset __isset; - - void __set_type(const Type::type val); - - void __set_type_length(const int32_t val); - - void __set_repetition_type(const FieldRepetitionType::type val); - - void __set_name(const std::string& val); - - void __set_num_children(const int32_t val); - - void __set_converted_type(const ConvertedType::type val); - - void __set_scale(const int32_t val); - - void __set_precision(const int32_t val); - - void __set_field_id(const int32_t val); - - void __set_logicalType(const LogicalType& val); - - bool operator == (const SchemaElement & rhs) const - { - if (__isset.type != rhs.__isset.type) - return false; - else if (__isset.type && !(type == rhs.type)) - return false; - if (__isset.type_length != rhs.__isset.type_length) - return false; - else if (__isset.type_length && !(type_length == rhs.type_length)) - return false; - if (__isset.repetition_type != rhs.__isset.repetition_type) - return false; - else if (__isset.repetition_type && !(repetition_type == rhs.repetition_type)) - return false; - if (!(name == rhs.name)) - return false; - if (__isset.num_children != rhs.__isset.num_children) - return false; - else if (__isset.num_children && !(num_children == rhs.num_children)) - return false; - if (__isset.converted_type != rhs.__isset.converted_type) - return false; - else if (__isset.converted_type && !(converted_type == rhs.converted_type)) - return false; - if (__isset.scale != rhs.__isset.scale) - return false; - else if (__isset.scale && !(scale == rhs.scale)) - return false; - if (__isset.precision != rhs.__isset.precision) - return false; - else if (__isset.precision && !(precision == rhs.precision)) - return false; - if (__isset.field_id != rhs.__isset.field_id) - return false; - else if (__isset.field_id && !(field_id == rhs.field_id)) - return false; - if (__isset.logicalType != rhs.__isset.logicalType) - return false; - else if (__isset.logicalType && !(logicalType == rhs.logicalType)) - return false; - return true; - } - bool operator != (const SchemaElement &rhs) const { - return !(*this == rhs); - } - - bool operator < (const SchemaElement & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(SchemaElement &a, SchemaElement &b); - -std::ostream& operator<<(std::ostream& out, const SchemaElement& obj); - -typedef struct _DataPageHeader__isset { - _DataPageHeader__isset() : statistics(false) {} - bool statistics :1; -} _DataPageHeader__isset; - -class DataPageHeader : public virtual ::apache::thrift::TBase { - public: - - DataPageHeader(const DataPageHeader&); - DataPageHeader& operator=(const DataPageHeader&); - DataPageHeader() : num_values(0), encoding((Encoding::type)0), definition_level_encoding((Encoding::type)0), repetition_level_encoding((Encoding::type)0) { - } - - virtual ~DataPageHeader() throw(); - int32_t num_values; - Encoding::type encoding; - Encoding::type definition_level_encoding; - Encoding::type repetition_level_encoding; - Statistics statistics; - - _DataPageHeader__isset __isset; - - void __set_num_values(const int32_t val); - - void __set_encoding(const Encoding::type val); - - void __set_definition_level_encoding(const Encoding::type val); - - void __set_repetition_level_encoding(const Encoding::type val); - - void __set_statistics(const Statistics& val); - - bool operator == (const DataPageHeader & rhs) const - { - if (!(num_values == rhs.num_values)) - return false; - if (!(encoding == rhs.encoding)) - return false; - if (!(definition_level_encoding == rhs.definition_level_encoding)) - return false; - if (!(repetition_level_encoding == rhs.repetition_level_encoding)) - return false; - if (__isset.statistics != rhs.__isset.statistics) - return false; - else if (__isset.statistics && !(statistics == rhs.statistics)) - return false; - return true; - } - bool operator != (const DataPageHeader &rhs) const { - return !(*this == rhs); - } - - bool operator < (const DataPageHeader & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(DataPageHeader &a, DataPageHeader &b); - -std::ostream& operator<<(std::ostream& out, const DataPageHeader& obj); - - -class IndexPageHeader : public virtual ::apache::thrift::TBase { - public: - - IndexPageHeader(const IndexPageHeader&); - IndexPageHeader& operator=(const IndexPageHeader&); - IndexPageHeader() { - } - - virtual ~IndexPageHeader() throw(); - - bool operator == (const IndexPageHeader & /* rhs */) const - { - return true; - } - bool operator != (const IndexPageHeader &rhs) const { - return !(*this == rhs); - } - - bool operator < (const IndexPageHeader & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(IndexPageHeader &a, IndexPageHeader &b); - -std::ostream& operator<<(std::ostream& out, const IndexPageHeader& obj); - -typedef struct _DictionaryPageHeader__isset { - _DictionaryPageHeader__isset() : is_sorted(false) {} - bool is_sorted :1; -} _DictionaryPageHeader__isset; - -class DictionaryPageHeader : public virtual ::apache::thrift::TBase { - public: - - DictionaryPageHeader(const DictionaryPageHeader&); - DictionaryPageHeader& operator=(const DictionaryPageHeader&); - DictionaryPageHeader() : num_values(0), encoding((Encoding::type)0), is_sorted(0) { - } - - virtual ~DictionaryPageHeader() throw(); - int32_t num_values; - Encoding::type encoding; - bool is_sorted; - - _DictionaryPageHeader__isset __isset; - - void __set_num_values(const int32_t val); - - void __set_encoding(const Encoding::type val); - - void __set_is_sorted(const bool val); - - bool operator == (const DictionaryPageHeader & rhs) const - { - if (!(num_values == rhs.num_values)) - return false; - if (!(encoding == rhs.encoding)) - return false; - if (__isset.is_sorted != rhs.__isset.is_sorted) - return false; - else if (__isset.is_sorted && !(is_sorted == rhs.is_sorted)) - return false; - return true; - } - bool operator != (const DictionaryPageHeader &rhs) const { - return !(*this == rhs); - } - - bool operator < (const DictionaryPageHeader & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(DictionaryPageHeader &a, DictionaryPageHeader &b); - -std::ostream& operator<<(std::ostream& out, const DictionaryPageHeader& obj); - -typedef struct _DataPageHeaderV2__isset { - _DataPageHeaderV2__isset() : is_compressed(true), statistics(false) {} - bool is_compressed :1; - bool statistics :1; -} _DataPageHeaderV2__isset; - -class DataPageHeaderV2 : public virtual ::apache::thrift::TBase { - public: - - DataPageHeaderV2(const DataPageHeaderV2&); - DataPageHeaderV2& operator=(const DataPageHeaderV2&); - DataPageHeaderV2() : num_values(0), num_nulls(0), num_rows(0), encoding((Encoding::type)0), definition_levels_byte_length(0), repetition_levels_byte_length(0), is_compressed(true) { - } - - virtual ~DataPageHeaderV2() throw(); - int32_t num_values; - int32_t num_nulls; - int32_t num_rows; - Encoding::type encoding; - int32_t definition_levels_byte_length; - int32_t repetition_levels_byte_length; - bool is_compressed; - Statistics statistics; - - _DataPageHeaderV2__isset __isset; - - void __set_num_values(const int32_t val); - - void __set_num_nulls(const int32_t val); - - void __set_num_rows(const int32_t val); - - void __set_encoding(const Encoding::type val); - - void __set_definition_levels_byte_length(const int32_t val); - - void __set_repetition_levels_byte_length(const int32_t val); - - void __set_is_compressed(const bool val); - - void __set_statistics(const Statistics& val); - - bool operator == (const DataPageHeaderV2 & rhs) const - { - if (!(num_values == rhs.num_values)) - return false; - if (!(num_nulls == rhs.num_nulls)) - return false; - if (!(num_rows == rhs.num_rows)) - return false; - if (!(encoding == rhs.encoding)) - return false; - if (!(definition_levels_byte_length == rhs.definition_levels_byte_length)) - return false; - if (!(repetition_levels_byte_length == rhs.repetition_levels_byte_length)) - return false; - if (__isset.is_compressed != rhs.__isset.is_compressed) - return false; - else if (__isset.is_compressed && !(is_compressed == rhs.is_compressed)) - return false; - if (__isset.statistics != rhs.__isset.statistics) - return false; - else if (__isset.statistics && !(statistics == rhs.statistics)) - return false; - return true; - } - bool operator != (const DataPageHeaderV2 &rhs) const { - return !(*this == rhs); - } - - bool operator < (const DataPageHeaderV2 & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(DataPageHeaderV2 &a, DataPageHeaderV2 &b); - -std::ostream& operator<<(std::ostream& out, const DataPageHeaderV2& obj); - - -class SplitBlockAlgorithm : public virtual ::apache::thrift::TBase { - public: - - SplitBlockAlgorithm(const SplitBlockAlgorithm&); - SplitBlockAlgorithm& operator=(const SplitBlockAlgorithm&); - SplitBlockAlgorithm() { - } - - virtual ~SplitBlockAlgorithm() throw(); - - bool operator == (const SplitBlockAlgorithm & /* rhs */) const - { - return true; - } - bool operator != (const SplitBlockAlgorithm &rhs) const { - return !(*this == rhs); - } - - bool operator < (const SplitBlockAlgorithm & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(SplitBlockAlgorithm &a, SplitBlockAlgorithm &b); - -std::ostream& operator<<(std::ostream& out, const SplitBlockAlgorithm& obj); - -typedef struct _BloomFilterAlgorithm__isset { - _BloomFilterAlgorithm__isset() : BLOCK(false) {} - bool BLOCK :1; -} _BloomFilterAlgorithm__isset; - -class BloomFilterAlgorithm : public virtual ::apache::thrift::TBase { - public: - - BloomFilterAlgorithm(const BloomFilterAlgorithm&); - BloomFilterAlgorithm& operator=(const BloomFilterAlgorithm&); - BloomFilterAlgorithm() { - } - - virtual ~BloomFilterAlgorithm() throw(); - SplitBlockAlgorithm BLOCK; - - _BloomFilterAlgorithm__isset __isset; - - void __set_BLOCK(const SplitBlockAlgorithm& val); - - bool operator == (const BloomFilterAlgorithm & rhs) const - { - if (__isset.BLOCK != rhs.__isset.BLOCK) - return false; - else if (__isset.BLOCK && !(BLOCK == rhs.BLOCK)) - return false; - return true; - } - bool operator != (const BloomFilterAlgorithm &rhs) const { - return !(*this == rhs); - } - - bool operator < (const BloomFilterAlgorithm & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(BloomFilterAlgorithm &a, BloomFilterAlgorithm &b); - -std::ostream& operator<<(std::ostream& out, const BloomFilterAlgorithm& obj); - - -class XxHash : public virtual ::apache::thrift::TBase { - public: - - XxHash(const XxHash&); - XxHash& operator=(const XxHash&); - XxHash() { - } - - virtual ~XxHash() throw(); - - bool operator == (const XxHash & /* rhs */) const - { - return true; - } - bool operator != (const XxHash &rhs) const { - return !(*this == rhs); - } - - bool operator < (const XxHash & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(XxHash &a, XxHash &b); - -std::ostream& operator<<(std::ostream& out, const XxHash& obj); - -typedef struct _BloomFilterHash__isset { - _BloomFilterHash__isset() : XXHASH(false) {} - bool XXHASH :1; -} _BloomFilterHash__isset; - -class BloomFilterHash : public virtual ::apache::thrift::TBase { - public: - - BloomFilterHash(const BloomFilterHash&); - BloomFilterHash& operator=(const BloomFilterHash&); - BloomFilterHash() { - } - - virtual ~BloomFilterHash() throw(); - XxHash XXHASH; - - _BloomFilterHash__isset __isset; - - void __set_XXHASH(const XxHash& val); - - bool operator == (const BloomFilterHash & rhs) const - { - if (__isset.XXHASH != rhs.__isset.XXHASH) - return false; - else if (__isset.XXHASH && !(XXHASH == rhs.XXHASH)) - return false; - return true; - } - bool operator != (const BloomFilterHash &rhs) const { - return !(*this == rhs); - } - - bool operator < (const BloomFilterHash & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(BloomFilterHash &a, BloomFilterHash &b); - -std::ostream& operator<<(std::ostream& out, const BloomFilterHash& obj); - - -class Uncompressed : public virtual ::apache::thrift::TBase { - public: - - Uncompressed(const Uncompressed&); - Uncompressed& operator=(const Uncompressed&); - Uncompressed() { - } - - virtual ~Uncompressed() throw(); - - bool operator == (const Uncompressed & /* rhs */) const - { - return true; - } - bool operator != (const Uncompressed &rhs) const { - return !(*this == rhs); - } - - bool operator < (const Uncompressed & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(Uncompressed &a, Uncompressed &b); - -std::ostream& operator<<(std::ostream& out, const Uncompressed& obj); - -typedef struct _BloomFilterCompression__isset { - _BloomFilterCompression__isset() : UNCOMPRESSED(false) {} - bool UNCOMPRESSED :1; -} _BloomFilterCompression__isset; - -class BloomFilterCompression : public virtual ::apache::thrift::TBase { - public: - - BloomFilterCompression(const BloomFilterCompression&); - BloomFilterCompression& operator=(const BloomFilterCompression&); - BloomFilterCompression() { - } - - virtual ~BloomFilterCompression() throw(); - Uncompressed UNCOMPRESSED; - - _BloomFilterCompression__isset __isset; - - void __set_UNCOMPRESSED(const Uncompressed& val); - - bool operator == (const BloomFilterCompression & rhs) const - { - if (__isset.UNCOMPRESSED != rhs.__isset.UNCOMPRESSED) - return false; - else if (__isset.UNCOMPRESSED && !(UNCOMPRESSED == rhs.UNCOMPRESSED)) - return false; - return true; - } - bool operator != (const BloomFilterCompression &rhs) const { - return !(*this == rhs); - } - - bool operator < (const BloomFilterCompression & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(BloomFilterCompression &a, BloomFilterCompression &b); - -std::ostream& operator<<(std::ostream& out, const BloomFilterCompression& obj); - - -class BloomFilterHeader : public virtual ::apache::thrift::TBase { - public: - - BloomFilterHeader(const BloomFilterHeader&); - BloomFilterHeader& operator=(const BloomFilterHeader&); - BloomFilterHeader() : numBytes(0) { - } - - virtual ~BloomFilterHeader() throw(); - int32_t numBytes; - BloomFilterAlgorithm algorithm; - BloomFilterHash hash; - BloomFilterCompression compression; - - void __set_numBytes(const int32_t val); - - void __set_algorithm(const BloomFilterAlgorithm& val); - - void __set_hash(const BloomFilterHash& val); - - void __set_compression(const BloomFilterCompression& val); - - bool operator == (const BloomFilterHeader & rhs) const - { - if (!(numBytes == rhs.numBytes)) - return false; - if (!(algorithm == rhs.algorithm)) - return false; - if (!(hash == rhs.hash)) - return false; - if (!(compression == rhs.compression)) - return false; - return true; - } - bool operator != (const BloomFilterHeader &rhs) const { - return !(*this == rhs); - } - - bool operator < (const BloomFilterHeader & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(BloomFilterHeader &a, BloomFilterHeader &b); - -std::ostream& operator<<(std::ostream& out, const BloomFilterHeader& obj); - -typedef struct _PageHeader__isset { - _PageHeader__isset() : crc(false), data_page_header(false), index_page_header(false), dictionary_page_header(false), data_page_header_v2(false) {} - bool crc :1; - bool data_page_header :1; - bool index_page_header :1; - bool dictionary_page_header :1; - bool data_page_header_v2 :1; -} _PageHeader__isset; - -class PageHeader : public virtual ::apache::thrift::TBase { - public: - - PageHeader(const PageHeader&); - PageHeader& operator=(const PageHeader&); - PageHeader() : type((PageType::type)0), uncompressed_page_size(0), compressed_page_size(0), crc(0) { - } - - virtual ~PageHeader() throw(); - PageType::type type; - int32_t uncompressed_page_size; - int32_t compressed_page_size; - int32_t crc; - DataPageHeader data_page_header; - IndexPageHeader index_page_header; - DictionaryPageHeader dictionary_page_header; - DataPageHeaderV2 data_page_header_v2; - - _PageHeader__isset __isset; - - void __set_type(const PageType::type val); - - void __set_uncompressed_page_size(const int32_t val); - - void __set_compressed_page_size(const int32_t val); - - void __set_crc(const int32_t val); - - void __set_data_page_header(const DataPageHeader& val); - - void __set_index_page_header(const IndexPageHeader& val); - - void __set_dictionary_page_header(const DictionaryPageHeader& val); - - void __set_data_page_header_v2(const DataPageHeaderV2& val); - - bool operator == (const PageHeader & rhs) const - { - if (!(type == rhs.type)) - return false; - if (!(uncompressed_page_size == rhs.uncompressed_page_size)) - return false; - if (!(compressed_page_size == rhs.compressed_page_size)) - return false; - if (__isset.crc != rhs.__isset.crc) - return false; - else if (__isset.crc && !(crc == rhs.crc)) - return false; - if (__isset.data_page_header != rhs.__isset.data_page_header) - return false; - else if (__isset.data_page_header && !(data_page_header == rhs.data_page_header)) - return false; - if (__isset.index_page_header != rhs.__isset.index_page_header) - return false; - else if (__isset.index_page_header && !(index_page_header == rhs.index_page_header)) - return false; - if (__isset.dictionary_page_header != rhs.__isset.dictionary_page_header) - return false; - else if (__isset.dictionary_page_header && !(dictionary_page_header == rhs.dictionary_page_header)) - return false; - if (__isset.data_page_header_v2 != rhs.__isset.data_page_header_v2) - return false; - else if (__isset.data_page_header_v2 && !(data_page_header_v2 == rhs.data_page_header_v2)) - return false; - return true; - } - bool operator != (const PageHeader &rhs) const { - return !(*this == rhs); - } - - bool operator < (const PageHeader & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(PageHeader &a, PageHeader &b); - -std::ostream& operator<<(std::ostream& out, const PageHeader& obj); - -typedef struct _KeyValue__isset { - _KeyValue__isset() : value(false) {} - bool value :1; -} _KeyValue__isset; - -class KeyValue : public virtual ::apache::thrift::TBase { - public: - - KeyValue(const KeyValue&); - KeyValue& operator=(const KeyValue&); - KeyValue() : key(), value() { - } - - virtual ~KeyValue() throw(); - std::string key; - std::string value; - - _KeyValue__isset __isset; - - void __set_key(const std::string& val); - - void __set_value(const std::string& val); - - bool operator == (const KeyValue & rhs) const - { - if (!(key == rhs.key)) - return false; - if (__isset.value != rhs.__isset.value) - return false; - else if (__isset.value && !(value == rhs.value)) - return false; - return true; - } - bool operator != (const KeyValue &rhs) const { - return !(*this == rhs); - } - - bool operator < (const KeyValue & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(KeyValue &a, KeyValue &b); - -std::ostream& operator<<(std::ostream& out, const KeyValue& obj); - - -class SortingColumn : public virtual ::apache::thrift::TBase { - public: - - SortingColumn(const SortingColumn&); - SortingColumn& operator=(const SortingColumn&); - SortingColumn() : column_idx(0), descending(0), nulls_first(0) { - } - - virtual ~SortingColumn() throw(); - int32_t column_idx; - bool descending; - bool nulls_first; - - void __set_column_idx(const int32_t val); - - void __set_descending(const bool val); - - void __set_nulls_first(const bool val); - - bool operator == (const SortingColumn & rhs) const - { - if (!(column_idx == rhs.column_idx)) - return false; - if (!(descending == rhs.descending)) - return false; - if (!(nulls_first == rhs.nulls_first)) - return false; - return true; - } - bool operator != (const SortingColumn &rhs) const { - return !(*this == rhs); - } - - bool operator < (const SortingColumn & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(SortingColumn &a, SortingColumn &b); - -std::ostream& operator<<(std::ostream& out, const SortingColumn& obj); - - -class PageEncodingStats : public virtual ::apache::thrift::TBase { - public: - - PageEncodingStats(const PageEncodingStats&); - PageEncodingStats& operator=(const PageEncodingStats&); - PageEncodingStats() : page_type((PageType::type)0), encoding((Encoding::type)0), count(0) { - } - - virtual ~PageEncodingStats() throw(); - PageType::type page_type; - Encoding::type encoding; - int32_t count; - - void __set_page_type(const PageType::type val); - - void __set_encoding(const Encoding::type val); - - void __set_count(const int32_t val); - - bool operator == (const PageEncodingStats & rhs) const - { - if (!(page_type == rhs.page_type)) - return false; - if (!(encoding == rhs.encoding)) - return false; - if (!(count == rhs.count)) - return false; - return true; - } - bool operator != (const PageEncodingStats &rhs) const { - return !(*this == rhs); - } - - bool operator < (const PageEncodingStats & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(PageEncodingStats &a, PageEncodingStats &b); - -std::ostream& operator<<(std::ostream& out, const PageEncodingStats& obj); - -typedef struct _ColumnMetaData__isset { - _ColumnMetaData__isset() : key_value_metadata(false), index_page_offset(false), dictionary_page_offset(false), statistics(false), encoding_stats(false), bloom_filter_offset(false) {} - bool key_value_metadata :1; - bool index_page_offset :1; - bool dictionary_page_offset :1; - bool statistics :1; - bool encoding_stats :1; - bool bloom_filter_offset :1; -} _ColumnMetaData__isset; - -class ColumnMetaData : public virtual ::apache::thrift::TBase { - public: - - ColumnMetaData(const ColumnMetaData&); - ColumnMetaData& operator=(const ColumnMetaData&); - ColumnMetaData() : type((Type::type)0), codec((CompressionCodec::type)0), num_values(0), total_uncompressed_size(0), total_compressed_size(0), data_page_offset(0), index_page_offset(0), dictionary_page_offset(0), bloom_filter_offset(0) { - } - - virtual ~ColumnMetaData() throw(); - Type::type type; - std::vector encodings; - std::vector path_in_schema; - CompressionCodec::type codec; - int64_t num_values; - int64_t total_uncompressed_size; - int64_t total_compressed_size; - std::vector key_value_metadata; - int64_t data_page_offset; - int64_t index_page_offset; - int64_t dictionary_page_offset; - Statistics statistics; - std::vector encoding_stats; - int64_t bloom_filter_offset; - - _ColumnMetaData__isset __isset; - - void __set_type(const Type::type val); - - void __set_encodings(const std::vector & val); - - void __set_path_in_schema(const std::vector & val); - - void __set_codec(const CompressionCodec::type val); - - void __set_num_values(const int64_t val); - - void __set_total_uncompressed_size(const int64_t val); - - void __set_total_compressed_size(const int64_t val); - - void __set_key_value_metadata(const std::vector & val); - - void __set_data_page_offset(const int64_t val); - - void __set_index_page_offset(const int64_t val); - - void __set_dictionary_page_offset(const int64_t val); - - void __set_statistics(const Statistics& val); - - void __set_encoding_stats(const std::vector & val); - - void __set_bloom_filter_offset(const int64_t val); - - bool operator == (const ColumnMetaData & rhs) const - { - if (!(type == rhs.type)) - return false; - if (!(encodings == rhs.encodings)) - return false; - if (!(path_in_schema == rhs.path_in_schema)) - return false; - if (!(codec == rhs.codec)) - return false; - if (!(num_values == rhs.num_values)) - return false; - if (!(total_uncompressed_size == rhs.total_uncompressed_size)) - return false; - if (!(total_compressed_size == rhs.total_compressed_size)) - return false; - if (__isset.key_value_metadata != rhs.__isset.key_value_metadata) - return false; - else if (__isset.key_value_metadata && !(key_value_metadata == rhs.key_value_metadata)) - return false; - if (!(data_page_offset == rhs.data_page_offset)) - return false; - if (__isset.index_page_offset != rhs.__isset.index_page_offset) - return false; - else if (__isset.index_page_offset && !(index_page_offset == rhs.index_page_offset)) - return false; - if (__isset.dictionary_page_offset != rhs.__isset.dictionary_page_offset) - return false; - else if (__isset.dictionary_page_offset && !(dictionary_page_offset == rhs.dictionary_page_offset)) - return false; - if (__isset.statistics != rhs.__isset.statistics) - return false; - else if (__isset.statistics && !(statistics == rhs.statistics)) - return false; - if (__isset.encoding_stats != rhs.__isset.encoding_stats) - return false; - else if (__isset.encoding_stats && !(encoding_stats == rhs.encoding_stats)) - return false; - if (__isset.bloom_filter_offset != rhs.__isset.bloom_filter_offset) - return false; - else if (__isset.bloom_filter_offset && !(bloom_filter_offset == rhs.bloom_filter_offset)) - return false; - return true; - } - bool operator != (const ColumnMetaData &rhs) const { - return !(*this == rhs); - } - - bool operator < (const ColumnMetaData & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(ColumnMetaData &a, ColumnMetaData &b); - -std::ostream& operator<<(std::ostream& out, const ColumnMetaData& obj); - - -class EncryptionWithFooterKey : public virtual ::apache::thrift::TBase { - public: - - EncryptionWithFooterKey(const EncryptionWithFooterKey&); - EncryptionWithFooterKey& operator=(const EncryptionWithFooterKey&); - EncryptionWithFooterKey() { - } - - virtual ~EncryptionWithFooterKey() throw(); - - bool operator == (const EncryptionWithFooterKey & /* rhs */) const - { - return true; - } - bool operator != (const EncryptionWithFooterKey &rhs) const { - return !(*this == rhs); - } - - bool operator < (const EncryptionWithFooterKey & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(EncryptionWithFooterKey &a, EncryptionWithFooterKey &b); - -std::ostream& operator<<(std::ostream& out, const EncryptionWithFooterKey& obj); - -typedef struct _EncryptionWithColumnKey__isset { - _EncryptionWithColumnKey__isset() : key_metadata(false) {} - bool key_metadata :1; -} _EncryptionWithColumnKey__isset; - -class EncryptionWithColumnKey : public virtual ::apache::thrift::TBase { - public: - - EncryptionWithColumnKey(const EncryptionWithColumnKey&); - EncryptionWithColumnKey& operator=(const EncryptionWithColumnKey&); - EncryptionWithColumnKey() : key_metadata() { - } - - virtual ~EncryptionWithColumnKey() throw(); - std::vector path_in_schema; - std::string key_metadata; - - _EncryptionWithColumnKey__isset __isset; - - void __set_path_in_schema(const std::vector & val); - - void __set_key_metadata(const std::string& val); - - bool operator == (const EncryptionWithColumnKey & rhs) const - { - if (!(path_in_schema == rhs.path_in_schema)) - return false; - if (__isset.key_metadata != rhs.__isset.key_metadata) - return false; - else if (__isset.key_metadata && !(key_metadata == rhs.key_metadata)) - return false; - return true; - } - bool operator != (const EncryptionWithColumnKey &rhs) const { - return !(*this == rhs); - } - - bool operator < (const EncryptionWithColumnKey & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(EncryptionWithColumnKey &a, EncryptionWithColumnKey &b); - -std::ostream& operator<<(std::ostream& out, const EncryptionWithColumnKey& obj); - -typedef struct _ColumnCryptoMetaData__isset { - _ColumnCryptoMetaData__isset() : ENCRYPTION_WITH_FOOTER_KEY(false), ENCRYPTION_WITH_COLUMN_KEY(false) {} - bool ENCRYPTION_WITH_FOOTER_KEY :1; - bool ENCRYPTION_WITH_COLUMN_KEY :1; -} _ColumnCryptoMetaData__isset; - -class ColumnCryptoMetaData : public virtual ::apache::thrift::TBase { - public: - - ColumnCryptoMetaData(const ColumnCryptoMetaData&); - ColumnCryptoMetaData& operator=(const ColumnCryptoMetaData&); - ColumnCryptoMetaData() { - } - - virtual ~ColumnCryptoMetaData() throw(); - EncryptionWithFooterKey ENCRYPTION_WITH_FOOTER_KEY; - EncryptionWithColumnKey ENCRYPTION_WITH_COLUMN_KEY; - - _ColumnCryptoMetaData__isset __isset; - - void __set_ENCRYPTION_WITH_FOOTER_KEY(const EncryptionWithFooterKey& val); - - void __set_ENCRYPTION_WITH_COLUMN_KEY(const EncryptionWithColumnKey& val); - - bool operator == (const ColumnCryptoMetaData & rhs) const - { - if (__isset.ENCRYPTION_WITH_FOOTER_KEY != rhs.__isset.ENCRYPTION_WITH_FOOTER_KEY) - return false; - else if (__isset.ENCRYPTION_WITH_FOOTER_KEY && !(ENCRYPTION_WITH_FOOTER_KEY == rhs.ENCRYPTION_WITH_FOOTER_KEY)) - return false; - if (__isset.ENCRYPTION_WITH_COLUMN_KEY != rhs.__isset.ENCRYPTION_WITH_COLUMN_KEY) - return false; - else if (__isset.ENCRYPTION_WITH_COLUMN_KEY && !(ENCRYPTION_WITH_COLUMN_KEY == rhs.ENCRYPTION_WITH_COLUMN_KEY)) - return false; - return true; - } - bool operator != (const ColumnCryptoMetaData &rhs) const { - return !(*this == rhs); - } - - bool operator < (const ColumnCryptoMetaData & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(ColumnCryptoMetaData &a, ColumnCryptoMetaData &b); - -std::ostream& operator<<(std::ostream& out, const ColumnCryptoMetaData& obj); - -typedef struct _ColumnChunk__isset { - _ColumnChunk__isset() : file_path(false), meta_data(false), offset_index_offset(false), offset_index_length(false), column_index_offset(false), column_index_length(false), crypto_metadata(false), encrypted_column_metadata(false) {} - bool file_path :1; - bool meta_data :1; - bool offset_index_offset :1; - bool offset_index_length :1; - bool column_index_offset :1; - bool column_index_length :1; - bool crypto_metadata :1; - bool encrypted_column_metadata :1; -} _ColumnChunk__isset; - -class ColumnChunk : public virtual ::apache::thrift::TBase { - public: - - ColumnChunk(const ColumnChunk&); - ColumnChunk& operator=(const ColumnChunk&); - ColumnChunk() : file_path(), file_offset(0), offset_index_offset(0), offset_index_length(0), column_index_offset(0), column_index_length(0), encrypted_column_metadata() { - } - - virtual ~ColumnChunk() throw(); - std::string file_path; - int64_t file_offset; - ColumnMetaData meta_data; - int64_t offset_index_offset; - int32_t offset_index_length; - int64_t column_index_offset; - int32_t column_index_length; - ColumnCryptoMetaData crypto_metadata; - std::string encrypted_column_metadata; - - _ColumnChunk__isset __isset; - - void __set_file_path(const std::string& val); - - void __set_file_offset(const int64_t val); - - void __set_meta_data(const ColumnMetaData& val); - - void __set_offset_index_offset(const int64_t val); - - void __set_offset_index_length(const int32_t val); - - void __set_column_index_offset(const int64_t val); - - void __set_column_index_length(const int32_t val); - - void __set_crypto_metadata(const ColumnCryptoMetaData& val); - - void __set_encrypted_column_metadata(const std::string& val); - - bool operator == (const ColumnChunk & rhs) const - { - if (__isset.file_path != rhs.__isset.file_path) - return false; - else if (__isset.file_path && !(file_path == rhs.file_path)) - return false; - if (!(file_offset == rhs.file_offset)) - return false; - if (__isset.meta_data != rhs.__isset.meta_data) - return false; - else if (__isset.meta_data && !(meta_data == rhs.meta_data)) - return false; - if (__isset.offset_index_offset != rhs.__isset.offset_index_offset) - return false; - else if (__isset.offset_index_offset && !(offset_index_offset == rhs.offset_index_offset)) - return false; - if (__isset.offset_index_length != rhs.__isset.offset_index_length) - return false; - else if (__isset.offset_index_length && !(offset_index_length == rhs.offset_index_length)) - return false; - if (__isset.column_index_offset != rhs.__isset.column_index_offset) - return false; - else if (__isset.column_index_offset && !(column_index_offset == rhs.column_index_offset)) - return false; - if (__isset.column_index_length != rhs.__isset.column_index_length) - return false; - else if (__isset.column_index_length && !(column_index_length == rhs.column_index_length)) - return false; - if (__isset.crypto_metadata != rhs.__isset.crypto_metadata) - return false; - else if (__isset.crypto_metadata && !(crypto_metadata == rhs.crypto_metadata)) - return false; - if (__isset.encrypted_column_metadata != rhs.__isset.encrypted_column_metadata) - return false; - else if (__isset.encrypted_column_metadata && !(encrypted_column_metadata == rhs.encrypted_column_metadata)) - return false; - return true; - } - bool operator != (const ColumnChunk &rhs) const { - return !(*this == rhs); - } - - bool operator < (const ColumnChunk & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(ColumnChunk &a, ColumnChunk &b); - -std::ostream& operator<<(std::ostream& out, const ColumnChunk& obj); - -typedef struct _RowGroup__isset { - _RowGroup__isset() : sorting_columns(false), file_offset(false), total_compressed_size(false), ordinal(false) {} - bool sorting_columns :1; - bool file_offset :1; - bool total_compressed_size :1; - bool ordinal :1; -} _RowGroup__isset; - -class RowGroup : public virtual ::apache::thrift::TBase { - public: - - RowGroup(const RowGroup&); - RowGroup& operator=(const RowGroup&); - RowGroup() : total_byte_size(0), num_rows(0), file_offset(0), total_compressed_size(0), ordinal(0) { - } - - virtual ~RowGroup() throw(); - std::vector columns; - int64_t total_byte_size; - int64_t num_rows; - std::vector sorting_columns; - int64_t file_offset; - int64_t total_compressed_size; - int16_t ordinal; - - _RowGroup__isset __isset; - - void __set_columns(const std::vector & val); - - void __set_total_byte_size(const int64_t val); - - void __set_num_rows(const int64_t val); - - void __set_sorting_columns(const std::vector & val); - - void __set_file_offset(const int64_t val); - - void __set_total_compressed_size(const int64_t val); - - void __set_ordinal(const int16_t val); - - bool operator == (const RowGroup & rhs) const - { - if (!(columns == rhs.columns)) - return false; - if (!(total_byte_size == rhs.total_byte_size)) - return false; - if (!(num_rows == rhs.num_rows)) - return false; - if (__isset.sorting_columns != rhs.__isset.sorting_columns) - return false; - else if (__isset.sorting_columns && !(sorting_columns == rhs.sorting_columns)) - return false; - if (__isset.file_offset != rhs.__isset.file_offset) - return false; - else if (__isset.file_offset && !(file_offset == rhs.file_offset)) - return false; - if (__isset.total_compressed_size != rhs.__isset.total_compressed_size) - return false; - else if (__isset.total_compressed_size && !(total_compressed_size == rhs.total_compressed_size)) - return false; - if (__isset.ordinal != rhs.__isset.ordinal) - return false; - else if (__isset.ordinal && !(ordinal == rhs.ordinal)) - return false; - return true; - } - bool operator != (const RowGroup &rhs) const { - return !(*this == rhs); - } - - bool operator < (const RowGroup & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(RowGroup &a, RowGroup &b); - -std::ostream& operator<<(std::ostream& out, const RowGroup& obj); - - -class TypeDefinedOrder : public virtual ::apache::thrift::TBase { - public: - - TypeDefinedOrder(const TypeDefinedOrder&); - TypeDefinedOrder& operator=(const TypeDefinedOrder&); - TypeDefinedOrder() { - } - - virtual ~TypeDefinedOrder() throw(); - - bool operator == (const TypeDefinedOrder & /* rhs */) const - { - return true; - } - bool operator != (const TypeDefinedOrder &rhs) const { - return !(*this == rhs); - } - - bool operator < (const TypeDefinedOrder & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(TypeDefinedOrder &a, TypeDefinedOrder &b); - -std::ostream& operator<<(std::ostream& out, const TypeDefinedOrder& obj); - -typedef struct _ColumnOrder__isset { - _ColumnOrder__isset() : TYPE_ORDER(false) {} - bool TYPE_ORDER :1; -} _ColumnOrder__isset; - -class ColumnOrder : public virtual ::apache::thrift::TBase { - public: - - ColumnOrder(const ColumnOrder&); - ColumnOrder& operator=(const ColumnOrder&); - ColumnOrder() { - } - - virtual ~ColumnOrder() throw(); - TypeDefinedOrder TYPE_ORDER; - - _ColumnOrder__isset __isset; - - void __set_TYPE_ORDER(const TypeDefinedOrder& val); - - bool operator == (const ColumnOrder & rhs) const - { - if (__isset.TYPE_ORDER != rhs.__isset.TYPE_ORDER) - return false; - else if (__isset.TYPE_ORDER && !(TYPE_ORDER == rhs.TYPE_ORDER)) - return false; - return true; - } - bool operator != (const ColumnOrder &rhs) const { - return !(*this == rhs); - } - - bool operator < (const ColumnOrder & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(ColumnOrder &a, ColumnOrder &b); - -std::ostream& operator<<(std::ostream& out, const ColumnOrder& obj); - - -class PageLocation : public virtual ::apache::thrift::TBase { - public: - - PageLocation(const PageLocation&); - PageLocation& operator=(const PageLocation&); - PageLocation() : offset(0), compressed_page_size(0), first_row_index(0) { - } - - virtual ~PageLocation() throw(); - int64_t offset; - int32_t compressed_page_size; - int64_t first_row_index; - - void __set_offset(const int64_t val); - - void __set_compressed_page_size(const int32_t val); - - void __set_first_row_index(const int64_t val); - - bool operator == (const PageLocation & rhs) const - { - if (!(offset == rhs.offset)) - return false; - if (!(compressed_page_size == rhs.compressed_page_size)) - return false; - if (!(first_row_index == rhs.first_row_index)) - return false; - return true; - } - bool operator != (const PageLocation &rhs) const { - return !(*this == rhs); - } - - bool operator < (const PageLocation & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(PageLocation &a, PageLocation &b); - -std::ostream& operator<<(std::ostream& out, const PageLocation& obj); - - -class OffsetIndex : public virtual ::apache::thrift::TBase { - public: - - OffsetIndex(const OffsetIndex&); - OffsetIndex& operator=(const OffsetIndex&); - OffsetIndex() { - } - - virtual ~OffsetIndex() throw(); - std::vector page_locations; - - void __set_page_locations(const std::vector & val); - - bool operator == (const OffsetIndex & rhs) const - { - if (!(page_locations == rhs.page_locations)) - return false; - return true; - } - bool operator != (const OffsetIndex &rhs) const { - return !(*this == rhs); - } - - bool operator < (const OffsetIndex & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(OffsetIndex &a, OffsetIndex &b); - -std::ostream& operator<<(std::ostream& out, const OffsetIndex& obj); - -typedef struct _ColumnIndex__isset { - _ColumnIndex__isset() : null_counts(false) {} - bool null_counts :1; -} _ColumnIndex__isset; - -class ColumnIndex : public virtual ::apache::thrift::TBase { - public: - - ColumnIndex(const ColumnIndex&); - ColumnIndex& operator=(const ColumnIndex&); - ColumnIndex() : boundary_order((BoundaryOrder::type)0) { - } - - virtual ~ColumnIndex() throw(); - std::vector null_pages; - std::vector min_values; - std::vector max_values; - BoundaryOrder::type boundary_order; - std::vector null_counts; - - _ColumnIndex__isset __isset; - - void __set_null_pages(const std::vector & val); - - void __set_min_values(const std::vector & val); - - void __set_max_values(const std::vector & val); - - void __set_boundary_order(const BoundaryOrder::type val); - - void __set_null_counts(const std::vector & val); - - bool operator == (const ColumnIndex & rhs) const - { - if (!(null_pages == rhs.null_pages)) - return false; - if (!(min_values == rhs.min_values)) - return false; - if (!(max_values == rhs.max_values)) - return false; - if (!(boundary_order == rhs.boundary_order)) - return false; - if (__isset.null_counts != rhs.__isset.null_counts) - return false; - else if (__isset.null_counts && !(null_counts == rhs.null_counts)) - return false; - return true; - } - bool operator != (const ColumnIndex &rhs) const { - return !(*this == rhs); - } - - bool operator < (const ColumnIndex & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(ColumnIndex &a, ColumnIndex &b); - -std::ostream& operator<<(std::ostream& out, const ColumnIndex& obj); - -typedef struct _AesGcmV1__isset { - _AesGcmV1__isset() : aad_prefix(false), aad_file_unique(false), supply_aad_prefix(false) {} - bool aad_prefix :1; - bool aad_file_unique :1; - bool supply_aad_prefix :1; -} _AesGcmV1__isset; - -class AesGcmV1 : public virtual ::apache::thrift::TBase { - public: - - AesGcmV1(const AesGcmV1&); - AesGcmV1& operator=(const AesGcmV1&); - AesGcmV1() : aad_prefix(), aad_file_unique(), supply_aad_prefix(0) { - } - - virtual ~AesGcmV1() throw(); - std::string aad_prefix; - std::string aad_file_unique; - bool supply_aad_prefix; - - _AesGcmV1__isset __isset; - - void __set_aad_prefix(const std::string& val); - - void __set_aad_file_unique(const std::string& val); - - void __set_supply_aad_prefix(const bool val); - - bool operator == (const AesGcmV1 & rhs) const - { - if (__isset.aad_prefix != rhs.__isset.aad_prefix) - return false; - else if (__isset.aad_prefix && !(aad_prefix == rhs.aad_prefix)) - return false; - if (__isset.aad_file_unique != rhs.__isset.aad_file_unique) - return false; - else if (__isset.aad_file_unique && !(aad_file_unique == rhs.aad_file_unique)) - return false; - if (__isset.supply_aad_prefix != rhs.__isset.supply_aad_prefix) - return false; - else if (__isset.supply_aad_prefix && !(supply_aad_prefix == rhs.supply_aad_prefix)) - return false; - return true; - } - bool operator != (const AesGcmV1 &rhs) const { - return !(*this == rhs); - } - - bool operator < (const AesGcmV1 & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(AesGcmV1 &a, AesGcmV1 &b); - -std::ostream& operator<<(std::ostream& out, const AesGcmV1& obj); - -typedef struct _AesGcmCtrV1__isset { - _AesGcmCtrV1__isset() : aad_prefix(false), aad_file_unique(false), supply_aad_prefix(false) {} - bool aad_prefix :1; - bool aad_file_unique :1; - bool supply_aad_prefix :1; -} _AesGcmCtrV1__isset; - -class AesGcmCtrV1 : public virtual ::apache::thrift::TBase { - public: - - AesGcmCtrV1(const AesGcmCtrV1&); - AesGcmCtrV1& operator=(const AesGcmCtrV1&); - AesGcmCtrV1() : aad_prefix(), aad_file_unique(), supply_aad_prefix(0) { - } - - virtual ~AesGcmCtrV1() throw(); - std::string aad_prefix; - std::string aad_file_unique; - bool supply_aad_prefix; - - _AesGcmCtrV1__isset __isset; - - void __set_aad_prefix(const std::string& val); - - void __set_aad_file_unique(const std::string& val); - - void __set_supply_aad_prefix(const bool val); - - bool operator == (const AesGcmCtrV1 & rhs) const - { - if (__isset.aad_prefix != rhs.__isset.aad_prefix) - return false; - else if (__isset.aad_prefix && !(aad_prefix == rhs.aad_prefix)) - return false; - if (__isset.aad_file_unique != rhs.__isset.aad_file_unique) - return false; - else if (__isset.aad_file_unique && !(aad_file_unique == rhs.aad_file_unique)) - return false; - if (__isset.supply_aad_prefix != rhs.__isset.supply_aad_prefix) - return false; - else if (__isset.supply_aad_prefix && !(supply_aad_prefix == rhs.supply_aad_prefix)) - return false; - return true; - } - bool operator != (const AesGcmCtrV1 &rhs) const { - return !(*this == rhs); - } - - bool operator < (const AesGcmCtrV1 & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(AesGcmCtrV1 &a, AesGcmCtrV1 &b); - -std::ostream& operator<<(std::ostream& out, const AesGcmCtrV1& obj); - -typedef struct _EncryptionAlgorithm__isset { - _EncryptionAlgorithm__isset() : AES_GCM_V1(false), AES_GCM_CTR_V1(false) {} - bool AES_GCM_V1 :1; - bool AES_GCM_CTR_V1 :1; -} _EncryptionAlgorithm__isset; - -class EncryptionAlgorithm : public virtual ::apache::thrift::TBase { - public: - - EncryptionAlgorithm(const EncryptionAlgorithm&); - EncryptionAlgorithm& operator=(const EncryptionAlgorithm&); - EncryptionAlgorithm() { - } - - virtual ~EncryptionAlgorithm() throw(); - AesGcmV1 AES_GCM_V1; - AesGcmCtrV1 AES_GCM_CTR_V1; - - _EncryptionAlgorithm__isset __isset; - - void __set_AES_GCM_V1(const AesGcmV1& val); - - void __set_AES_GCM_CTR_V1(const AesGcmCtrV1& val); - - bool operator == (const EncryptionAlgorithm & rhs) const - { - if (__isset.AES_GCM_V1 != rhs.__isset.AES_GCM_V1) - return false; - else if (__isset.AES_GCM_V1 && !(AES_GCM_V1 == rhs.AES_GCM_V1)) - return false; - if (__isset.AES_GCM_CTR_V1 != rhs.__isset.AES_GCM_CTR_V1) - return false; - else if (__isset.AES_GCM_CTR_V1 && !(AES_GCM_CTR_V1 == rhs.AES_GCM_CTR_V1)) - return false; - return true; - } - bool operator != (const EncryptionAlgorithm &rhs) const { - return !(*this == rhs); - } - - bool operator < (const EncryptionAlgorithm & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(EncryptionAlgorithm &a, EncryptionAlgorithm &b); - -std::ostream& operator<<(std::ostream& out, const EncryptionAlgorithm& obj); - -typedef struct _FileMetaData__isset { - _FileMetaData__isset() : key_value_metadata(false), created_by(false), column_orders(false), encryption_algorithm(false), footer_signing_key_metadata(false) {} - bool key_value_metadata :1; - bool created_by :1; - bool column_orders :1; - bool encryption_algorithm :1; - bool footer_signing_key_metadata :1; -} _FileMetaData__isset; - -class FileMetaData : public virtual ::apache::thrift::TBase { - public: - - FileMetaData(const FileMetaData&); - FileMetaData& operator=(const FileMetaData&); - FileMetaData() : version(0), num_rows(0), created_by(), footer_signing_key_metadata() { - } - - virtual ~FileMetaData() throw(); - int32_t version; - std::vector schema; - int64_t num_rows; - std::vector row_groups; - std::vector key_value_metadata; - std::string created_by; - std::vector column_orders; - EncryptionAlgorithm encryption_algorithm; - std::string footer_signing_key_metadata; - - _FileMetaData__isset __isset; - - void __set_version(const int32_t val); - - void __set_schema(const std::vector & val); - - void __set_num_rows(const int64_t val); - - void __set_row_groups(const std::vector & val); - - void __set_key_value_metadata(const std::vector & val); - - void __set_created_by(const std::string& val); - - void __set_column_orders(const std::vector & val); - - void __set_encryption_algorithm(const EncryptionAlgorithm& val); - - void __set_footer_signing_key_metadata(const std::string& val); - - bool operator == (const FileMetaData & rhs) const - { - if (!(version == rhs.version)) - return false; - if (!(schema == rhs.schema)) - return false; - if (!(num_rows == rhs.num_rows)) - return false; - if (!(row_groups == rhs.row_groups)) - return false; - if (__isset.key_value_metadata != rhs.__isset.key_value_metadata) - return false; - else if (__isset.key_value_metadata && !(key_value_metadata == rhs.key_value_metadata)) - return false; - if (__isset.created_by != rhs.__isset.created_by) - return false; - else if (__isset.created_by && !(created_by == rhs.created_by)) - return false; - if (__isset.column_orders != rhs.__isset.column_orders) - return false; - else if (__isset.column_orders && !(column_orders == rhs.column_orders)) - return false; - if (__isset.encryption_algorithm != rhs.__isset.encryption_algorithm) - return false; - else if (__isset.encryption_algorithm && !(encryption_algorithm == rhs.encryption_algorithm)) - return false; - if (__isset.footer_signing_key_metadata != rhs.__isset.footer_signing_key_metadata) - return false; - else if (__isset.footer_signing_key_metadata && !(footer_signing_key_metadata == rhs.footer_signing_key_metadata)) - return false; - return true; - } - bool operator != (const FileMetaData &rhs) const { - return !(*this == rhs); - } - - bool operator < (const FileMetaData & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(FileMetaData &a, FileMetaData &b); - -std::ostream& operator<<(std::ostream& out, const FileMetaData& obj); - -typedef struct _FileCryptoMetaData__isset { - _FileCryptoMetaData__isset() : key_metadata(false) {} - bool key_metadata :1; -} _FileCryptoMetaData__isset; - -class FileCryptoMetaData : public virtual ::apache::thrift::TBase { - public: - - FileCryptoMetaData(const FileCryptoMetaData&); - FileCryptoMetaData& operator=(const FileCryptoMetaData&); - FileCryptoMetaData() : key_metadata() { - } - - virtual ~FileCryptoMetaData() throw(); - EncryptionAlgorithm encryption_algorithm; - std::string key_metadata; - - _FileCryptoMetaData__isset __isset; - - void __set_encryption_algorithm(const EncryptionAlgorithm& val); - - void __set_key_metadata(const std::string& val); - - bool operator == (const FileCryptoMetaData & rhs) const - { - if (!(encryption_algorithm == rhs.encryption_algorithm)) - return false; - if (__isset.key_metadata != rhs.__isset.key_metadata) - return false; - else if (__isset.key_metadata && !(key_metadata == rhs.key_metadata)) - return false; - return true; - } - bool operator != (const FileCryptoMetaData &rhs) const { - return !(*this == rhs); - } - - bool operator < (const FileCryptoMetaData & ) const; - - uint32_t read(::apache::thrift::protocol::TProtocol* iprot); - uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; - - virtual void printTo(std::ostream& out) const; -}; - -void swap(FileCryptoMetaData &a, FileCryptoMetaData &b); - -std::ostream& operator<<(std::ostream& out, const FileCryptoMetaData& obj); - -}} // namespace - -#endif From 9a3663c38a7455a699ca082dfc4e81a3ddaa359a Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Thu, 17 Dec 2020 10:35:51 -0800 Subject: [PATCH 10/85] Bump Avro to 1.10.1 (#1235) This PR bumps Avro to 1.10.1. Signed-off-by: Yong Tang --- WORKSPACE | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/WORKSPACE b/WORKSPACE index d9fb5e3d4..e0308fb88 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -80,11 +80,11 @@ http_archive( http_archive( name = "avro", build_file = "//third_party:avro.BUILD", - sha256 = "e382ac6685544ae9539084793ac0a4ffd377ba476ea756439625552e14d212b0", - strip_prefix = "avro-release-1.9.1/lang/c++", + sha256 = "8fd1f850ce37e60835e6d8335c0027a959aaa316773da8a9660f7d33a66ac142", + strip_prefix = "avro-release-1.10.1/lang/c++", urls = [ - "https://storage.googleapis.com/mirror.tensorflow.org/github.com/apache/avro/archive/release-1.9.1.tar.gz", - "https://github.com/apache/avro/archive/release-1.9.1.tar.gz", + "https://storage.googleapis.com/mirror.tensorflow.org/github.com/apache/avro/archive/release-1.10.1.tar.gz", + "https://github.com/apache/avro/archive/release-1.10.1.tar.gz", ], ) From 2e6936f5158512477c05af0e2768cb65cb5cab1c Mon Sep 17 00:00:00 2001 From: Vo Van Nghia Date: Fri, 18 Dec 2020 20:48:00 +0700 Subject: [PATCH 11/85] Add emulator for gcs (#1234) * Bump com_github_googleapis_google_cloud_cpp to `1.21.0` * Add gcs testbench * Bump `libcurl` to `7.69.1` --- .github/workflows/api.yml | 2 + WORKSPACE | 16 +- tests/test_gcloud/test_gcs.sh | 14 +- tests/test_gcloud/testbench/README.md | 15 + tests/test_gcloud/testbench/error_response.py | 36 + tests/test_gcloud/testbench/gcs_bucket.py | 258 ++++++ tests/test_gcloud/testbench/gcs_object.py | 770 ++++++++++++++++++ tests/test_gcloud/testbench/requirements.txt | 5 + tests/test_gcloud/testbench/testbench.py | 599 ++++++++++++++ .../test_gcloud/testbench/testbench_utils.py | 324 ++++++++ tests/test_gcs_eager.py | 15 +- third_party/curl.BUILD | 4 +- 12 files changed, 2035 insertions(+), 23 deletions(-) mode change 100644 => 100755 tests/test_gcloud/test_gcs.sh create mode 100644 tests/test_gcloud/testbench/README.md create mode 100644 tests/test_gcloud/testbench/error_response.py create mode 100644 tests/test_gcloud/testbench/gcs_bucket.py create mode 100644 tests/test_gcloud/testbench/gcs_object.py create mode 100644 tests/test_gcloud/testbench/requirements.txt create mode 100644 tests/test_gcloud/testbench/testbench.py create mode 100644 tests/test_gcloud/testbench/testbench_utils.py diff --git a/.github/workflows/api.yml b/.github/workflows/api.yml index 0fc774ba3..fe43d4e16 100644 --- a/.github/workflows/api.yml +++ b/.github/workflows/api.yml @@ -50,6 +50,7 @@ jobs: python -m pytest -s -v tests/test_http_eager.py python -m pytest -s -v tests/test_s3_eager.py python -m pytest -s -v tests/test_azure.py + python -m pytest -s -v tests/test_gcs_eager.py linux: name: Linux ${{ matrix.python }} + ${{ matrix.version }} @@ -85,6 +86,7 @@ jobs: python -m pytest -s -v tests/test_http_eager.py python -m pytest -s -v tests/test_s3_eager.py python -m pytest -s -v tests/test_azure.py + python -m pytest -s -v tests/test_gcs_eager.py windows: name: Windows ${{ matrix.python }} + ${{ matrix.version }} diff --git a/WORKSPACE b/WORKSPACE index e0308fb88..dd980c7ef 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -419,11 +419,11 @@ http_archive( http_archive( name = "curl", build_file = "//third_party:curl.BUILD", - sha256 = "e9c37986337743f37fd14fe8737f246e97aec94b39d1b71e8a5973f72a9fc4f5", - strip_prefix = "curl-7.60.0", + sha256 = "01ae0c123dee45b01bbaef94c0bc00ed2aec89cb2ee0fd598e0d302a6b5e0a98", + strip_prefix = "curl-7.69.1", urls = [ - "https://storage.googleapis.com/mirror.tensorflow.org/curl.haxx.se/download/curl-7.60.0.tar.gz", - "https://curl.haxx.se/download/curl-7.60.0.tar.gz", + "https://storage.googleapis.com/mirror.tensorflow.org/curl.haxx.se/download/curl-7.69.1.tar.gz", + "https://curl.haxx.se/download/curl-7.69.1.tar.gz", ], ) @@ -584,11 +584,11 @@ http_archive( "@com_github_curl_curl": "@curl", "@com_github_nlohmann_json": "@nlohmann_json_lib", }, - sha256 = "ff82045b9491f0d880fc8e5c83fd9542eafb156dcac9ff8c6209ced66ed2a7f0", - strip_prefix = "google-cloud-cpp-1.17.1", + sha256 = "14bf9bf97431b890e0ae5dca8f8904841d4883b8596a7108a42f5700ae58d711", + strip_prefix = "google-cloud-cpp-1.21.0", urls = [ - "https://storage.googleapis.com/mirror.tensorflow.org/github.com/googleapis/google-cloud-cpp/archive/v1.17.1.tar.gz", - "https://github.com/googleapis/google-cloud-cpp/archive/v1.17.1.tar.gz", + "https://storage.googleapis.com/mirror.tensorflow.org/github.com/googleapis/google-cloud-cpp/archive/v1.21.0.tar.gz", + "https://github.com/googleapis/google-cloud-cpp/archive/v1.21.0.tar.gz", ], ) diff --git a/tests/test_gcloud/test_gcs.sh b/tests/test_gcloud/test_gcs.sh old mode 100644 new mode 100755 index 235ed1a12..57aa0ac85 --- a/tests/test_gcloud/test_gcs.sh +++ b/tests/test_gcloud/test_gcs.sh @@ -4,7 +4,7 @@ set -o pipefail if [ "$#" -eq 1 ]; then container=$1 docker pull python:3.8 - docker run -d --rm --net=host --name=$container -v $PWD:/v -w /v python:3.8 bash -x -c 'python3 -m pip install gcloud-storage-emulator==0.3.0 && gcloud-storage-emulator start --port=9099' + docker run -d --rm --net=host --name=$container -v $PWD:/v -w /v python:3.8 bash -x -c 'python3 -m pip install -r /v/tests/test_gcloud/testbench/requirements.txt && gunicorn --bind "0.0.0.0:9099" --worker-class gevent --chdir "/v/tests/test_gcloud/testbench" testbench:application' echo wait 30 secs until gcs emulator is up and running sleep 30 exit 0 @@ -12,9 +12,11 @@ fi export PATH=$(python3 -m site --user-base)/bin:$PATH -python3 -m pip install gcloud-storage-emulator==0.3.0 - -gcloud-storage-emulator start --port=9099 & - +python3 -m pip install -r tests/test_gcloud/testbench/requirements.txt +echo starting gcs-testbench +gunicorn --bind "0.0.0.0:9099" \ + --worker-class gevent \ + --chdir "tests/test_gcloud/testbench" \ + testbench:application & sleep 30 # Wait for storage emulator to start -echo gcs emulator started successfully +echo gcs-testbench started successfully diff --git a/tests/test_gcloud/testbench/README.md b/tests/test_gcloud/testbench/README.md new file mode 100644 index 000000000..ddc3971b3 --- /dev/null +++ b/tests/test_gcloud/testbench/README.md @@ -0,0 +1,15 @@ +# GCS Testbench + +This is a minimal testbench for GCS. It only supports data operation and creating/listing/deleteing bucket. + +## Install Dependencies + +```bash +pip install -r requirements.txt +``` + +## Run Test Bench + +```bash +gunicorn --bind "0.0.0.0:9099" --worker-class gevent --chdir "tests/test_gcs/testbench" testbench:application +``` diff --git a/tests/test_gcloud/testbench/error_response.py b/tests/test_gcloud/testbench/error_response.py new file mode 100644 index 000000000..8d6a5816f --- /dev/null +++ b/tests/test_gcloud/testbench/error_response.py @@ -0,0 +1,36 @@ +# Copyright 2018 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. +"""A helper class to send error responses in the storage client test bench.""" + +import flask + + +class ErrorResponse(Exception): + """Simplify generation of error responses.""" + + status_code = 400 + + def __init__(self, message, status_code=None, payload=None): + Exception.__init__(self) + self.message = message + if status_code is not None: + self.status_code = status_code + self.payload = payload + + def as_response(self): + kv = dict(self.payload or ()) + kv["message"] = self.message + response = flask.jsonify(kv) + response.status_code = self.status_code + return response diff --git a/tests/test_gcloud/testbench/gcs_bucket.py b/tests/test_gcloud/testbench/gcs_bucket.py new file mode 100644 index 000000000..1fcba3992 --- /dev/null +++ b/tests/test_gcloud/testbench/gcs_bucket.py @@ -0,0 +1,258 @@ +# Copyright 2018 Google Inc. +# +# 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. +"""Implement a class to simulate GCS buckets.""" + +import base64 +import error_response +import flask +import gcs_object +import json +import re +import testbench_utils +import time + + +class GcsBucket: + """Represent a GCS Bucket.""" + + def __init__(self, gcs_url, name): + self.name = name + self.gcs_url = gcs_url + now = time.gmtime(time.time()) + timestamp = time.strftime("%Y-%m-%dT%H:%M:%SZ", now) + self.metadata = { + "timeCreated": timestamp, + "updated": timestamp, + "metageneration": "0", + "name": self.name, + "location": "US", + "storageClass": "STANDARD", + "etag": "XYZ=", + "labels": {"foo": "bar", "baz": "qux"}, + "owner": {"entity": "project-owners-123456789", "entityId": ""}, + } + self.resumable_uploads = {} + + def versioning_enabled(self): + """Return True if versioning is enabled for this Bucket.""" + v = self.metadata.get("versioning", None) + if v is None: + return False + return v.get("enabled", False) + + def check_preconditions(self, request): + """Verify that the preconditions in request are met. + + :param request:flask.Request the contents of the HTTP request. + :rtype:NoneType + :raises:ErrorResponse if the request does not pass the preconditions, + for example, the request has a `ifMetagenerationMatch` restriction + that is not met. + """ + + metageneration_match = request.args.get("ifMetagenerationMatch") + metageneration_not_match = request.args.get("ifMetagenerationNotMatch") + metageneration = self.metadata.get("metageneration") + + if ( + metageneration_not_match is not None + and metageneration_not_match == metageneration + ): + raise error_response.ErrorResponse( + "Precondition Failed (metageneration = %s)" % metageneration, + status_code=412, + ) + + if metageneration_match is not None and metageneration_match != metageneration: + raise error_response.ErrorResponse( + "Precondition Failed (metageneration = %s)" % metageneration, + status_code=412, + ) + + def create_resumable_upload(self, upload_url, request): + """Capture the details for a resumable upload. + + :param upload_url: str the base URL for uploads. + :param request: flask.Request the original http request. + :return: the HTTP response to send back. + """ + x_upload_content_type = request.headers.get( + "x-upload-content-type", "application/octet-stream" + ) + x_upload_content_length = request.headers.get("x-upload-content-length") + expected_bytes = None + if x_upload_content_length: + expected_bytes = int(x_upload_content_length) + + if request.args.get("name") is not None and len(request.data): + raise error_response.ErrorResponse( + "The name argument is only supported for empty payloads", + status_code=400, + ) + if len(request.data): + metadata = json.loads(request.data) + else: + metadata = {"name": request.args.get("name")} + + if metadata.get("name") is None: + raise error_response.ErrorResponse( + "Missing object name argument", status_code=400 + ) + metadata.setdefault("contentType", x_upload_content_type) + upload = { + "metadata": metadata, + "instructions": request.headers.get("x-goog-testbench-instructions"), + "fields": request.args.get("fields"), + "next_byte": 0, + "expected_bytes": expected_bytes, + "object_name": metadata.get("name"), + "media": b"", + "transfer": set(), + "done": False, + } + # Capture the preconditions, including those that are None. + for precondition in [ + "ifGenerationMatch", + "ifGenerationNotMatch", + "ifMetagenerationMatch", + "ifMetagenerationNotMatch", + ]: + upload[precondition] = request.args.get(precondition) + upload_id = base64.b64encode(bytearray(metadata.get("name"), "utf-8")).decode( + "utf-8" + ) + self.resumable_uploads[upload_id] = upload + location = "{}?uploadType=resumable&upload_id={}".format(upload_url, upload_id) + response = flask.make_response("") + response.headers["Location"] = location + return response + + def receive_upload_chunk(self, gcs_url, request): + """Receive a new upload chunk. + + :param gcs_url: str the base URL for the service. + :param request: flask.Request the original http request. + :return: the HTTP response. + """ + upload_id = request.args.get("upload_id") + if upload_id is None: + raise error_response.ErrorResponse( + "Missing upload_id in resumable_upload_chunk", status_code=400 + ) + upload = self.resumable_uploads.get(upload_id) + if upload is None: + raise error_response.ErrorResponse( + "Cannot find resumable upload %s" % upload_id, status_code=404 + ) + # Be gracious in what you accept, if the Content-Range header is not + # set we assume it is a good header and it is the end of the file. + next_byte = upload["next_byte"] + upload["transfer"].add(request.environ.get("HTTP_TRANSFER_ENCODING", "")) + end = next_byte + len(request.data) + total = end + final_chunk = False + payload = testbench_utils.extract_media(request) + content_range = request.headers.get("content-range") + if content_range is not None: + if content_range.startswith("bytes */*"): + # This is just a query to resume an upload, if it is done, return + # the completed upload payload and an empty range header. + response = flask.make_response(upload.get("payload", "")) + if next_byte > 1 and not upload["done"]: + response.headers["Range"] = "bytes=0-%d" % (next_byte - 1) + response.status_code = 200 if upload["done"] else 308 + return response + match = re.match(r"bytes \*/(\*|[0-9]+)", content_range) + if match: + if match.group(1) == "*": + total = 0 + else: + total = int(match.group(1)) + final_chunk = True + else: + match = re.match(r"bytes ([0-9]+)-([0-9]+)\/(\*|[0-9]+)", content_range) + if not match: + raise error_response.ErrorResponse( + "Invalid Content-Range in upload %s" % content_range, + status_code=400, + ) + begin = int(match.group(1)) + end = int(match.group(2)) + if match.group(3) == "*": + total = 0 + else: + total = int(match.group(3)) + final_chunk = True + + if begin != next_byte: + raise error_response.ErrorResponse( + "Mismatched data range, expected data at %d, got %d" + % (next_byte, begin), + status_code=400, + ) + if len(payload) != end - begin + 1: + raise error_response.ErrorResponse( + "Mismatched data range (%d) vs. received data (%d)" + % (end - begin + 1, len(payload)), + status_code=400, + ) + + upload["media"] = upload.get("media", b"") + payload + next_byte = len(upload.get("media", "")) + upload["next_byte"] = next_byte + response_payload = "" + if final_chunk and next_byte >= total: + expected_bytes = upload["expected_bytes"] + if expected_bytes is not None and expected_bytes != total: + raise error_response.ErrorResponse( + "X-Upload-Content-Length" + "validation failed. Expected=%d, got %d." % (expected_bytes, total) + ) + upload["done"] = True + object_name = upload.get("object_name") + object_path, blob = testbench_utils.get_object( + self.name, object_name, gcs_object.GcsObject(self.name, object_name) + ) + # Release a few resources to control memory usage. + original_metadata = upload.pop("metadata", None) + media = upload.pop("media", None) + blob.check_preconditions_by_value( + upload.get("ifGenerationMatch"), + upload.get("ifGenerationNotMatch"), + upload.get("ifMetagenerationMatch"), + upload.get("ifMetagenerationNotMatch"), + ) + if upload.pop("instructions", None) == "inject-upload-data-error": + media = testbench_utils.corrupt_media(media) + revision = blob.insert_resumable(gcs_url, request, media, original_metadata) + revision.metadata.setdefault("metadata", {}) + revision.metadata["metadata"]["x_testbench_transfer_encoding"] = ":".join( + upload["transfer"] + ) + response_payload = testbench_utils.filter_fields_from_response( + upload.get("fields"), revision.metadata + ) + upload["payload"] = response_payload + testbench_utils.insert_object(object_path, blob) + + response = flask.make_response(response_payload) + if next_byte == 0: + response.headers["Range"] = "bytes=0-0" + else: + response.headers["Range"] = "bytes=0-%d" % (next_byte - 1) + if upload.get("done", False): + response.status_code = 200 + else: + response.status_code = 308 + return response diff --git a/tests/test_gcloud/testbench/gcs_object.py b/tests/test_gcloud/testbench/gcs_object.py new file mode 100644 index 000000000..96acf19de --- /dev/null +++ b/tests/test_gcloud/testbench/gcs_object.py @@ -0,0 +1,770 @@ +# Copyright 2018 Google Inc. +# +# 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. +"""Implement a class to simulate GCS objects.""" + +import base64 +import crc32c +import error_response +import hashlib +import json +import struct +import testbench_utils +import time + + +class GcsObjectVersion: + """Represent a single revision of a GCS Object.""" + + def __init__(self, gcs_url, bucket_name, name, generation, request, media): + """Initialize a new object revision. + + :param gcs_url:str the base URL for the GCS service. + :param bucket_name:str the name of the bucket that contains the object. + :param name:str the name of the object. + :param generation:int the generation number for this object. + :param request:flask.Request the contents of the HTTP request. + :param media:str the contents of the object. + """ + self.gcs_url = gcs_url + self.bucket_name = bucket_name + self.name = name + self.generation = str(generation) + self.object_id = bucket_name + "/o/" + name + "/" + str(generation) + now = time.gmtime(time.time()) + timestamp = time.strftime("%Y-%m-%dT%H:%M:%SZ", now) + self.media = media + instructions = request.headers.get("x-goog-testbench-instructions") + if instructions == "inject-upload-data-error": + self.media = testbench_utils.corrupt_media(media) + + self.metadata = { + "timeCreated": timestamp, + "updated": timestamp, + "metageneration": "0", + "generation": str(generation), + "location": "US", + "storageClass": "STANDARD", + "size": str(len(self.media)), + "etag": "XYZ=", + "owner": {"entity": "project-owners-123456789", "entityId": ""}, + "md5Hash": base64.b64encode(hashlib.md5(self.media).digest()).decode( + "utf-8" + ), + "crc32c": base64.b64encode( + struct.pack(">I", crc32c.crc32(self.media)) + ).decode("utf-8"), + } + if request.headers.get("content-type") is not None: + self.metadata["contentType"] = request.headers.get("content-type") + + def update_from_metadata(self, metadata): + """Update from a metadata dictionary. + + :param metadata:dict a dictionary with new metadata values. + :rtype:NoneType + """ + tmp = self.metadata.copy() + tmp.update(metadata) + tmp["bucket"] = tmp.get("bucket", self.name) + tmp["name"] = tmp.get("name", self.name) + now = time.gmtime(time.time()) + timestamp = time.strftime("%Y-%m-%dT%H:%M:%SZ", now) + # Some values cannot be changed via updates, so we always reset them. + tmp.update( + { + "kind": "storage#object", + "bucket": self.bucket_name, + "name": self.name, + "id": self.object_id, + "selfLink": self.gcs_url + self.name, + "projectNumber": "123456789", + "updated": timestamp, + } + ) + tmp["metageneration"] = str(int(tmp.get("metageneration", "0")) + 1) + self.metadata = tmp + self._validate_hashes() + + def _validate_hashes(self): + """Validate the md5Hash and crc32c fields against the stored media.""" + self._validate_md5_hash() + self._validate_crc32c() + + def _validate_md5_hash(self): + """Validate the md5Hash field against the stored media.""" + actual = self.metadata.get("md5Hash", "") + expected = base64.b64encode(hashlib.md5(self.media).digest()).decode("utf-8") + if actual != expected: + raise error_response.ErrorResponse( + "Mismatched MD5 hash expected={}, actual={}".format(expected, actual) + ) + + def _validate_crc32c(self): + """Validate the crc32c field against the stored media.""" + actual = self.metadata.get("crc32c", "") + expected = base64.b64encode(struct.pack(">I", crc32c.crc32(self.media))).decode( + "utf-8" + ) + if actual != expected: + raise error_response.ErrorResponse( + "Mismatched CRC32C checksum expected={}, actual={}".format( + expected, actual + ) + ) + + def validate_encryption_for_read(self, request, prefix="x-goog-encryption"): + """Verify that the request includes the correct encryption keys. + + :param request:flask.Request the http request. + :param prefix: str the prefix shared by the encryption headers, + typically 'x-goog-encryption', but for rewrite requests it can be + 'x-goog-copy-source-encryption'. + :rtype:NoneType + """ + key_header = prefix + "-key" + hash_header = prefix + "-key-sha256" + algo_header = prefix + "-algorithm" + encryption = self.metadata.get("customerEncryption") + if encryption is None: + # The object is not encrypted, no key is needed. + if request.headers.get(key_header) is None: + return + else: + # The data is not encrypted, sending an encryption key is an + # error. + testbench_utils.raise_csek_error() + # The data is encrypted, the key must be present, match, and match its + # hash. + key_header_value = request.headers.get(key_header) + hash_header_value = request.headers.get(hash_header) + algo_header_value = request.headers.get(algo_header) + testbench_utils.validate_customer_encryption_headers( + key_header_value, hash_header_value, algo_header_value + ) + if encryption.get("keySha256") != hash_header_value: + testbench_utils.raise_csek_error() + + def _capture_customer_encryption(self, request): + """Capture the customer-supplied encryption key, if any. + + :param request:flask.Request the http request. + :rtype:NoneType + """ + if request.headers.get("x-goog-encryption-key") is None: + return + prefix = "x-goog-encryption" + key_header = prefix + "-key" + hash_header = prefix + "-key-sha256" + algo_header = prefix + "-algorithm" + key_header_value = request.headers.get(key_header) + hash_header_value = request.headers.get(hash_header) + algo_header_value = request.headers.get(algo_header) + testbench_utils.validate_customer_encryption_headers( + key_header_value, hash_header_value, algo_header_value + ) + self.metadata["customerEncryption"] = { + "encryptionAlgorithm": algo_header_value, + "keySha256": hash_header_value, + } + + def x_goog_hash_header(self): + """Return the value for the x-goog-hash header.""" + hashes = { + "md5": self.metadata.get("md5Hash", ""), + "crc32c": self.metadata.get("crc32c", ""), + } + hashes = ["{}={}".format(key, val) for key, val in hashes.items() if val] + return ",".join(hashes) + + +class GcsObject: + """Represent a GCS Object, including all its revisions.""" + + def __init__(self, bucket_name, name): + """Initialize a fake GCS Blob. + + :param bucket_name:str the bucket that will contain the new object. + :param name:str the name of the new object. + """ + self.bucket_name = bucket_name + self.name = name + # A counter to create new generation numbers for the object revisions. + # Note that 0 is an invalid generation number. The application can use + # ifGenerationMatch=0 as a pre-condition that means "object does not + # exist". + self.generation_generator = 0 + self.current_generation = None + self.revisions = {} + self.rewrite_token_generator = 0 + self.rewrite_operations = {} + + def get_revision(self, request, version_field_name="generation"): + """Get the information about a particular object revision or raise. + + :param request:flask.Request the contents of the http request. + :param version_field_name:str the name of the generation + parameter, typically 'generation', but sometimes 'sourceGeneration'. + :return: the object revision. + :rtype: GcsObjectVersion + :raises:ErrorResponse if the request contains an invalid generation + number. + """ + generation = request.args.get(version_field_name) + if generation is None: + return self.get_latest() + version = self.revisions.get(generation) + if version is None: + raise error_response.ErrorResponse( + "Precondition Failed: generation %s not found" % generation + ) + return version + + def del_revision(self, request): + """Delete a version of a fake GCS Blob. + + :param request:flask.Request the contents of the HTTP request. + :return: True if the object entry in the Bucket should be deleted. + :rtype: bool + """ + generation = request.args.get("generation") or self.current_generation + if generation is None: + return True + self.revisions.pop(generation) + if len(self.revisions) == 0: + self.current_generation = None + return True + self.current_generation = sorted(self.revisions.keys())[-1] + return False + + @classmethod + def _remove_non_writable_keys(cls, metadata): + """Remove the keys from metadata (an update or patch) that are not + writable. + + Both `Objects: patch` and `Objects: update` either ignore non-writable + keys or return 400 if the key does not match the current value. In + the testbench we simply always ignore them, to make life easier. + + :param metadata:dict a dictionary representing a patch or + update to the metadata. + :return metadata but with only any non-writable keys removed. + :rtype: dict + """ + writeable_keys = { + "acl", + "cacheControl", + "contentDisposition", + "contentEncoding", + "contentLanguage", + "contentType", + "eventBasedHold", + "metadata", + "temporaryHold", + "storageClass", + "customTime", + } + # Cannot change `metadata` while we are iterating over it, so we make + # a copy + keys = [key for key in metadata.keys()] + for key in keys: + if key not in writeable_keys: + metadata.pop(key, None) + return metadata + + def get_revision_by_generation(self, generation): + """Get object revision by generation or None if not found. + + :param generation:int + :return: the object revision by generation or None. + :rtype:GcsObjectRevision + """ + return self.revisions.get(str(generation), None) + + def get_latest(self): + return self.revisions.get(self.current_generation, None) + + def check_preconditions_by_value( + self, + generation_match, + generation_not_match, + metageneration_match, + metageneration_not_match, + ): + """Verify that the given precondition values are met.""" + current_generation = self.current_generation or "0" + if generation_match is not None and generation_match != current_generation: + raise error_response.ErrorResponse("Precondition Failed", status_code=412) + # This object does not exist (yet), testing in this case is special. + if ( + generation_not_match is not None + and generation_not_match == current_generation + ): + raise error_response.ErrorResponse("Precondition Failed", status_code=412) + + if self.current_generation is None: + if metageneration_match is not None or metageneration_not_match is not None: + raise error_response.ErrorResponse( + "Precondition Failed", status_code=412 + ) + return + + current = self.revisions.get(current_generation) + if current is None: + raise error_response.ErrorResponse("Object not found", status_code=404) + metageneration = current.metadata.get("metageneration") + if ( + metageneration_not_match is not None + and metageneration_not_match == metageneration + ): + raise error_response.ErrorResponse("Precondition Failed", status_code=412) + if metageneration_match is not None and metageneration_match != metageneration: + raise error_response.ErrorResponse("Precondition Failed", status_code=412) + + def check_preconditions( + self, + request, + if_generation_match="ifGenerationMatch", + if_generation_not_match="ifGenerationNotMatch", + if_metageneration_match="ifMetagenerationMatch", + if_metageneration_not_match="ifMetagenerationNotMatch", + ): + """Verify that the preconditions in request are met. + + :param request:flask.Request the http request. + :param if_generation_match:str the name of the generation match + parameter name, typically 'ifGenerationMatch', but sometimes + 'ifSourceGenerationMatch'. + :param if_generation_not_match:str the name of the generation not-match + parameter name, typically 'ifGenerationNotMatch', but sometimes + 'ifSourceGenerationNotMatch'. + :param if_metageneration_match:str the name of the metageneration match + parameter name, typically 'ifMetagenerationMatch', but sometimes + 'ifSourceMetagenerationMatch'. + :param if_metageneration_not_match:str the name of the metageneration + not-match parameter name, typically 'ifMetagenerationNotMatch', but + sometimes 'ifSourceMetagenerationNotMatch'. + :rtype:NoneType + """ + generation_match = request.args.get(if_generation_match) + generation_not_match = request.args.get(if_generation_not_match) + metageneration_match = request.args.get(if_metageneration_match) + metageneration_not_match = request.args.get(if_metageneration_not_match) + self.check_preconditions_by_value( + generation_match, + generation_not_match, + metageneration_match, + metageneration_not_match, + ) + + def _insert_revision(self, revision): + """Insert a new revision that has been initialized and checked. + + :param revision: GcsObjectVersion the new revision to insert. + :rtype:NoneType + """ + update = {str(self.generation_generator): revision} + bucket = testbench_utils.lookup_bucket(self.bucket_name) + if not bucket.versioning_enabled(): + self.revisions = update + else: + self.revisions.update(update) + self.current_generation = str(self.generation_generator) + + def insert(self, gcs_url, request): + """Insert a new revision based on the give flask request. + + :param gcs_url:str the root URL for the fake GCS service. + :param request:flask.Request the contents of the HTTP request. + :return: the newly created object version. + :rtype: GcsObjectVersion + """ + media = testbench_utils.extract_media(request) + self.generation_generator += 1 + revision = GcsObjectVersion( + gcs_url, + self.bucket_name, + self.name, + self.generation_generator, + request, + media, + ) + meta = revision.metadata.setdefault("metadata", {}) + meta["x_testbench_upload"] = "simple" + self._insert_revision(revision) + return revision + + def insert_multipart(self, gcs_url, request, resource, media_headers, media_body): + """Insert a new revision based on the give flask request. + + :param gcs_url:str the root URL for the fake GCS service. + :param request:flask.Request the contents of the HTTP request. + :param resource:dict JSON resource with object metadata. + :param media_headers:dict media headers in a multi-part upload. + :param media_body:str object data in a multi-part upload. + :return: the newly created object version. + :rtype: GcsObjectVersion + """ + # There are two ways to specify the content-type, the 'content-type' + # header and the resource['contentType'] field. They must be consistent, + # and the service generates an error when they are not. + if ( + resource.get("contentType") is not None + and media_headers.get("content-type") is not None + and resource.get("contentType") != media_headers.get("content-type") + ): + raise error_response.ErrorResponse( + ( + "Content-Type specified in the upload (%s) does not match" + + "contentType specified in the metadata (%s)." + ) + % (media_headers.get("content-type"), resource.get("contentType")), + status_code=400, + ) + # Set the contentType in the resource from the header. Note that if both + # are set they have the same value. + resource.setdefault("contentType", media_headers.get("content-type")) + self.generation_generator += 1 + revision = GcsObjectVersion( + gcs_url, + self.bucket_name, + self.name, + self.generation_generator, + request, + media_body, + ) + meta = revision.metadata.setdefault("metadata", {}) + meta["x_testbench_upload"] = "multipart" + if "md5Hash" in resource: + # We should return `x_testbench_md5` only when the user enables + # `MD5Hash` computations. + meta["x_testbench_md5"] = resource.get("md5Hash") + meta["x_testbench_crc32c"] = resource.get("crc32c", "") + # Apply any overrides from the resource object part. + revision.update_from_metadata(resource) + self._insert_revision(revision) + return revision + + def insert_resumable(self, gcs_url, request, media, resource): + """Implement the final insert for a resumable upload. + + :param gcs_url:str the root URL for the fake GCS service. + :param request:flask.Request the contents of the HTTP request. + :param media:str the media for the object. + :param resource:dict the metadata for the object. + :return: the newly created object version. + :rtype: GcsObjectVersion + """ + self.generation_generator += 1 + revision = GcsObjectVersion( + gcs_url, + self.bucket_name, + self.name, + self.generation_generator, + request, + media, + ) + meta = revision.metadata.setdefault("metadata", {}) + meta["x_testbench_upload"] = "resumable" + meta["x_testbench_md5"] = resource.get("md5Hash", "") + meta["x_testbench_crc32c"] = resource.get("crc32c", "") + # Apply any overrides from the resource object part. + revision.update_from_metadata(resource) + self._insert_revision(revision) + return revision + + def insert_xml(self, gcs_url, request): + """Implement the insert operation using the XML API. + + :param gcs_url:str the root URL for the fake GCS service. + :param request:flask.Request the contents of the HTTP request. + :return: the newly created object version. + :rtype: GcsObjectVersion + """ + media = testbench_utils.extract_media(request) + self.generation_generator += 1 + goog_hash = request.headers.get("x-goog-hash") + md5hash = None + crc32c = None + if goog_hash is not None: + for hash in goog_hash.split(","): + if hash.startswith("md5="): + md5hash = hash[4:] + if hash.startswith("crc32c="): + crc32c = hash[7:] + revision = GcsObjectVersion( + gcs_url, + self.bucket_name, + self.name, + self.generation_generator, + request, + media, + ) + meta = revision.metadata.setdefault("metadata", {}) + meta["x_testbench_upload"] = "xml" + if md5hash is not None: + meta["x_testbench_md5"] = md5hash + revision.update_from_metadata({"md5Hash": md5hash}) + if crc32c is not None: + meta["x_testbench_crc32c"] = crc32c + revision.update_from_metadata({"crc32c": crc32c}) + self._insert_revision(revision) + return revision + + def copy_from(self, gcs_url, request, source_revision): + """Insert a new revision based on the give flask request. + + :param gcs_url:str the root URL for the fake GCS service. + :param request:flask.Request the contents of the HTTP request. + :param source_revision:GcsObjectVersion the source object version to + copy from. + :return: the newly created object version. + :rtype: GcsObjectVersion + """ + self.generation_generator += 1 + source_revision.validate_encryption_for_read(request) + revision = GcsObjectVersion( + gcs_url, + self.bucket_name, + self.name, + self.generation_generator, + request, + source_revision.media, + ) + metadata = json.loads(request.data) + revision.update_from_metadata(metadata) + self._insert_revision(revision) + return revision + + def compose_from(self, gcs_url, request, composed_media): + """Compose a new revision based on the give flask request. + + :param gcs_url:str the root URL for the fake GCS service. + :param request:flask.Request the contents of the HTTP request. + :param composed_media:str contents of the composed object + :return: the newly created object version. + :rtype: GcsObjectVersion + """ + self.generation_generator += 1 + revision = GcsObjectVersion( + gcs_url, + self.bucket_name, + self.name, + self.generation_generator, + request, + composed_media, + ) + payload = json.loads(request.data) + if payload.get("destination") is not None: + revision.update_from_metadata(payload.get("destination")) + # The server often discards the MD5 Hash when composing objects, we can + # easily maintain them in the testbench, but dropping them helps us + # detect bugs sooner. + revision.metadata.pop("md5Hash") + self._insert_revision(revision) + return revision + + @classmethod + def rewrite_fixed_args(cls): + """The arguments that should not change between requests for the same + rewrite operation.""" + return [ + "destinationKmsKeyName", + "destinationPredefinedAcl", + "ifGenerationMatch", + "ifGenerationNotMatch", + "ifMetagenerationMatch", + "ifMetagenerationNotMatch", + "ifSourceGenerationMatch", + "ifSourceGenerationNotMatch", + "ifSourceMetagenerationMatch", + "ifSourceMetagenerationNotMatch", + "maxBytesRewrittenPerCall", + "projection", + "sourceGeneration", + "userProject", + ] + + @classmethod + def capture_rewrite_operation_arguments( + cls, request, destination_bucket, destination_object + ): + """Captures the arguments used to validate related rewrite calls. + + :rtype:dict + """ + original_arguments = {} + for arg in GcsObject.rewrite_fixed_args(): + original_arguments[arg] = request.args.get(arg) + original_arguments.update( + { + "destination_bucket": destination_bucket, + "destination_object": destination_object, + } + ) + return original_arguments + + @classmethod + def make_rewrite_token( + cls, operation, destination_bucket, destination_object, generation + ): + """Create a new rewrite token for the given operation.""" + return base64.b64encode( + bytearray( + "/".join( + [ + str(operation.get("id")), + destination_bucket, + destination_object, + str(generation), + str(operation.get("bytes_rewritten")), + ] + ), + "utf-8", + ) + ).decode("utf-8") + + def make_rewrite_operation(self, request, destination_bucket, destination_object): + """Create a new rewrite token for `Objects: rewrite`.""" + generation = request.args.get("sourceGeneration") + if generation is None: + generation = str(self.generation_generator) + else: + generation = generation + + self.rewrite_token_generator = self.rewrite_token_generator + 1 + body = json.loads(request.data) + original_arguments = self.capture_rewrite_operation_arguments( + request, destination_object, destination_object + ) + operation = { + "id": self.rewrite_token_generator, + "original_arguments": original_arguments, + "actual_generation": generation, + "bytes_rewritten": 0, + "body": body, + } + token = GcsObject.make_rewrite_token( + operation, destination_bucket, destination_object, generation + ) + return token, operation + + def rewrite_finish(self, gcs_url, request, body, source): + """Complete a rewrite from `source` into this object. + + :param gcs_url:str the root URL for the fake GCS service. + :param request:flask.Request the contents of the HTTP request. + :param body:dict the HTTP payload, parsed via json.loads() + :param source:GcsObjectVersion the source object version. + :return: the newly created object version. + :rtype: GcsObjectVersion + """ + media = source.media + self.check_preconditions(request) + self.generation_generator += 1 + revision = GcsObjectVersion( + gcs_url, + self.bucket_name, + self.name, + self.generation_generator, + request, + media, + ) + revision.update_from_metadata(body) + self._insert_revision(revision) + return revision + + def rewrite_step(self, gcs_url, request, destination_bucket, destination_object): + """Execute an iteration of `Objects: rewrite. + + Objects: rewrite may need to be called multiple times before it + succeeds. Only objects in the same location, with the same encryption, + are guaranteed to complete in a single request. + + The implementation simulates some, but not all, the behaviors of the + server, in particular, only rewrites within the same bucket and smaller + than 1MiB complete immediately. + + :param gcs_url:str the root URL for the fake GCS service. + :param request:flask.Request the contents of the HTTP request. + :param destination_bucket:str where will the object be placed after the + rewrite operation completes. + :param destination_object:str the name of the object when the rewrite + operation completes. + :return: a dictionary prepared for JSON encoding of a + `Objects: rewrite` response. + :rtype:dict + """ + body = json.loads(request.data) + rewrite_token = request.args.get("rewriteToken") + if rewrite_token is not None and rewrite_token != "": + # Note that we remove the rewrite operation, not just look it up. + # That way if the operation completes in this call, and/or fails, + # it is already removed. We need to insert it with a new token + # anyway, so this makes sense. + rewrite = self.rewrite_operations.pop(rewrite_token, None) + if rewrite is None: + raise error_response.ErrorResponse( + "Invalid or expired token in rewrite", status_code=410 + ) + else: + rewrite_token, rewrite = self.make_rewrite_operation( + request, destination_bucket, destination_bucket + ) + + # Compare the difference to the original arguments, on the first call + # this is a waste, but the code is easier to follow. + current_arguments = self.capture_rewrite_operation_arguments( + request, destination_bucket, destination_object + ) + diff = set(current_arguments) ^ set(rewrite.get("original_arguments")) + if len(diff) != 0: + raise error_response.ErrorResponse( + "Mismatched arguments to rewrite", status_code=412 + ) + + # This will raise if the version is deleted while the operation is in + # progress. + source = self.get_revision_by_generation(rewrite.get("actual_generation")) + source.validate_encryption_for_read( + request, prefix="x-goog-copy-source-encryption" + ) + bytes_rewritten = rewrite.get("bytes_rewritten") + bytes_rewritten += 1024 * 1024 + result = {"kind": "storage#rewriteResponse", "objectSize": len(source.media)} + if bytes_rewritten >= len(source.media): + bytes_rewritten = len(source.media) + rewrite["bytes_rewritten"] = bytes_rewritten + # Success, the operation completed. Return the new object: + object_path, destination = testbench_utils.get_object( + destination_bucket, + destination_object, + GcsObject(destination_bucket, destination_object), + ) + revision = destination.rewrite_finish(gcs_url, request, body, source) + testbench_utils.insert_object(object_path, destination) + result["done"] = True + result["resource"] = revision.metadata + rewrite_token = "" + else: + rewrite["bytes_rewritten"] = bytes_rewritten + rewrite_token = GcsObject.make_rewrite_token( + rewrite, destination_bucket, destination_object, source.generation + ) + self.rewrite_operations[rewrite_token] = rewrite + result["done"] = False + + result.update( + {"totalBytesRewritten": bytes_rewritten, "rewriteToken": rewrite_token} + ) + return result diff --git a/tests/test_gcloud/testbench/requirements.txt b/tests/test_gcloud/testbench/requirements.txt new file mode 100644 index 000000000..415ce8662 --- /dev/null +++ b/tests/test_gcloud/testbench/requirements.txt @@ -0,0 +1,5 @@ +crc32c==2.1 +flask==1.1.2 +greenlet==0.4.17 +gevent==20.9.0 +gunicorn==20.0.4 diff --git a/tests/test_gcloud/testbench/testbench.py b/tests/test_gcloud/testbench/testbench.py new file mode 100644 index 000000000..290fbb405 --- /dev/null +++ b/tests/test_gcloud/testbench/testbench.py @@ -0,0 +1,599 @@ +# Copyright 2018 Google Inc. +# +# 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. +"""A test bench for the Google Cloud Storage C++ Client Library.""" + +import argparse +import error_response +import flask +import gcs_bucket +import gcs_object +import json +import os +import re +import testbench_utils +import time +import sys +from werkzeug import serving +from werkzeug.middleware.dispatcher import DispatcherMiddleware + + +root = flask.Flask(__name__, subdomain_matching=True) +root.debug = True + + +@root.route("/") +def index(): + """Default handler for the test bench.""" + return "OK" + + +@root.route("/", subdomain="") +def root_get_object(bucket_name, object_name): + return xml_get_object(bucket_name, object_name) + + +@root.route("//", subdomain="") +def root_get_object_with_bucket(bucket_name, object_name): + return xml_get_object(bucket_name, object_name) + + +@root.route("/", subdomain="", methods=["PUT"]) +def root_put_object(bucket_name, object_name): + return xml_put_object(flask.request.host_url, bucket_name, object_name) + + +@root.route("//", subdomain="", methods=["PUT"]) +def root_put_object_with_bucket(bucket_name, object_name): + return xml_put_object(flask.request.host_url, bucket_name, object_name) + + +@root.errorhandler(error_response.ErrorResponse) +def root_error(error): + return error.as_response() + + +# Define the WSGI application to handle bucket requests. +GCS_HANDLER_PATH = "/storage/v1" +gcs = flask.Flask(__name__) +gcs.debug = True + + +def insert_magic_bucket(base_url): + if len(testbench_utils.all_buckets()) == 0: + bucket_name = os.environ.get( + "GOOGLE_CLOUD_CPP_STORAGE_TEST_BUCKET_NAME", "test-bucket" + ) + bucket = gcs_bucket.GcsBucket(base_url, bucket_name) + testbench_utils.insert_bucket(bucket_name, bucket) + + +@gcs.route("/") +def gcs_index(): + """The default handler for GCS requests.""" + return "OK" + + +@gcs.errorhandler(error_response.ErrorResponse) +def gcs_error(error): + return error.as_response() + + +@gcs.route("/b") +def buckets_list(): + """Implement the 'Buckets: list' API: return the Buckets in a project.""" + base_url = flask.url_for("gcs_index", _external=True) + project = flask.request.args.get("project") + if project is None or project.endswith("-"): + raise error_response.ErrorResponse( + "Invalid or missing project id in `Buckets: list`" + ) + insert_magic_bucket(base_url) + result = {"next_page_token": "", "items": []} + for name, b in testbench_utils.all_buckets(): + result["items"].append(b.metadata) + return testbench_utils.filtered_response(flask.request, result) + + +@gcs.route("/b", methods=["POST"]) +def buckets_insert(): + """Implement the 'Buckets: insert' API: create a new Bucket.""" + base_url = flask.url_for("gcs_index", _external=True) + insert_magic_bucket(base_url) + payload = json.loads(flask.request.data) + bucket_name = payload.get("name") + if bucket_name is None: + raise error_response.ErrorResponse( + "Missing bucket name in `Buckets: insert`", status_code=412 + ) + if testbench_utils.has_bucket(bucket_name): + raise error_response.ErrorResponse( + "Bucket %s already exists" % bucket_name, status_code=400 + ) + bucket = gcs_bucket.GcsBucket(base_url, bucket_name) + testbench_utils.insert_bucket(bucket_name, bucket) + return testbench_utils.filtered_response(flask.request, bucket.metadata) + + +@gcs.route("/b/") +def buckets_get(bucket_name): + """Implement the 'Buckets: get' API: return the metadata for a bucket.""" + base_url = flask.url_for("gcs_index", _external=True) + insert_magic_bucket(base_url) + bucket = testbench_utils.lookup_bucket(bucket_name) + bucket.check_preconditions(flask.request) + return testbench_utils.filtered_response(flask.request, bucket.metadata) + + +@gcs.route("/b/", methods=["DELETE"]) +def buckets_delete(bucket_name): + """Implement the 'Buckets: delete' API.""" + bucket = testbench_utils.lookup_bucket(bucket_name) + bucket.check_preconditions(flask.request) + testbench_utils.delete_bucket(bucket_name) + return testbench_utils.filtered_response(flask.request, {}) + + +@gcs.route("/b//o") +def objects_list(bucket_name): + """Implement the 'Objects: list' API: return the objects in a bucket.""" + # Lookup the bucket, if this fails the bucket does not exist, and this + # function should return an error. + base_url = flask.url_for("gcs_index", _external=True) + insert_magic_bucket(base_url) + _ = testbench_utils.lookup_bucket(bucket_name) + result = {"next_page_token": "", "items": [], "prefixes:": []} + versions_parameter = flask.request.args.get("versions") + all_versions = versions_parameter is not None and bool(versions_parameter) + prefixes = set() + prefix = flask.request.args.get("prefix", "", type("")) + delimiter = flask.request.args.get("delimiter", "", type("")) + start_offset = flask.request.args.get("startOffset", "", type("")) + end_offset = flask.request.args.get("endOffset", "", type("")) + bucket_link = bucket_name + "/o/" + for name, o in testbench_utils.all_objects(): + if name.find(bucket_link + prefix) != 0: + continue + if o.get_latest() is None: + continue + # We assume `delimiter` has only one character. + if name[len(bucket_link) :] < start_offset: + continue + if end_offset != "" and name[len(bucket_link) :] >= end_offset: + continue + delimiter_index = name.find(delimiter, len(bucket_link + prefix)) + if delimiter != "" and delimiter_index > 0: + # We don't want to include `bucket_link` in the returned prefix. + prefixes.add(name[len(bucket_link) : delimiter_index + 1]) + continue + if all_versions: + for object_version in o.revisions.values(): + result["items"].append(object_version.metadata) + else: + result["items"].append(o.get_latest().metadata) + result["prefixes"] = list(prefixes) + return testbench_utils.filtered_response(flask.request, result) + + +@gcs.route( + "/b//o//copyTo/b//o/", + methods=["POST"], +) +def objects_copy(source_bucket, source_object, destination_bucket, destination_object): + """Implement the 'Objects: copy' API, copy an object.""" + object_path, blob = testbench_utils.lookup_object(source_bucket, source_object) + blob.check_preconditions( + flask.request, + if_generation_match="ifSourceGenerationMatch", + if_generation_not_match="ifSourceGenerationNotMatch", + if_metageneration_match="ifSourceMetagenerationMatch", + if_metageneration_not_match="ifSourceMetagenerationNotMatch", + ) + source_revision = blob.get_revision(flask.request, "sourceGeneration") + if source_revision is None: + raise error_response.ErrorResponse( + "Revision not found %s" % object_path, status_code=404 + ) + + destination_path, destination = testbench_utils.get_object( + destination_bucket, + destination_object, + gcs_object.GcsObject(destination_bucket, destination_object), + ) + base_url = flask.url_for("gcs_index", _external=True) + current_version = destination.copy_from(base_url, flask.request, source_revision) + testbench_utils.insert_object(destination_path, destination) + return testbench_utils.filtered_response(flask.request, current_version.metadata) + + +@gcs.route( + "/b//o//rewriteTo/b//o/", + methods=["POST"], +) +def objects_rewrite( + source_bucket, source_object, destination_bucket, destination_object +): + """Implement the 'Objects: rewrite' API.""" + base_url = flask.url_for("gcs_index", _external=True) + insert_magic_bucket(base_url) + object_path, blob = testbench_utils.lookup_object(source_bucket, source_object) + blob.check_preconditions( + flask.request, + if_generation_match="ifSourceGenerationMatch", + if_generation_not_match="ifSourceGenerationNotMatch", + if_metageneration_match="ifSourceMetagenerationMatch", + if_metageneration_not_match="ifSourceMetagenerationNotMatch", + ) + response = blob.rewrite_step( + base_url, flask.request, destination_bucket, destination_object + ) + return testbench_utils.filtered_response(flask.request, response) + + +def objects_get_common(bucket_name, object_name, revision): + # Respect the Range: header, if present. + range_header = flask.request.headers.get("range") + response_payload = revision.media + begin = 0 + end = len(response_payload) + if range_header is not None: + m = re.match("bytes=([0-9]+)-([0-9]+)", range_header) + if m: + begin = int(m.group(1)) + end = int(m.group(2)) + response_payload = response_payload[begin : end + 1] + m = re.match("bytes=([0-9]+)-$", range_header) + if m: + begin = int(m.group(1)) + response_payload = response_payload[begin:] + m = re.match("bytes=-([0-9]+)$", range_header) + if m: + last = int(m.group(1)) + response_payload = response_payload[-last:] + # Process custom headers to test error conditions. + instructions = flask.request.headers.get("x-goog-testbench-instructions") + if instructions == "return-broken-stream": + + def streamer(): + chunk_size = 64 * 1024 + for r in range(0, len(response_payload), chunk_size): + if r > 1024 * 1024: + print("\n\n###### EXIT to simulate crash\n") + sys.exit(1) + time.sleep(0.1) + chunk_end = min(r + chunk_size, len(response_payload)) + yield response_payload[r:chunk_end] + + length = len(response_payload) + content_range = "bytes %d-%d/%d" % (begin, end - 1, length) + headers = { + "Content-Range": content_range, + "Content-Length": length, + "x-goog-hash": revision.x_goog_hash_header(), + "x-goog-generation": revision.generation, + } + return flask.Response(streamer(), status=200, headers=headers) + + if instructions == "return-corrupted-data": + response_payload = testbench_utils.corrupt_media(response_payload) + + if instructions is not None and instructions.startswith("stall-always"): + length = len(response_payload) + content_range = "bytes %d-%d/%d" % (begin, end - 1, length) + + def streamer(): + chunk_size = 16 * 1024 + for r in range(begin, end, chunk_size): + chunk_end = min(r + chunk_size, end) + if r == begin: + time.sleep(10) + yield response_payload[r:chunk_end] + + headers = { + "Content-Range": content_range, + "x-goog-hash": revision.x_goog_hash_header(), + "x-goog-generation": revision.generation, + } + return flask.Response(streamer(), status=200, headers=headers) + + if instructions == "stall-at-256KiB" and begin == 0: + length = len(response_payload) + content_range = "bytes %d-%d/%d" % (begin, end - 1, length) + + def streamer(): + chunk_size = 16 * 1024 + for r in range(begin, end, chunk_size): + chunk_end = min(r + chunk_size, end) + if r == 256 * 1024: + time.sleep(10) + yield response_payload[r:chunk_end] + + headers = { + "Content-Range": content_range, + "x-goog-hash": revision.x_goog_hash_header(), + "x-goog-generation": revision.generation, + } + return flask.Response(streamer(), status=200, headers=headers) + + if instructions is not None and instructions.startswith("return-503-after-256K"): + length = len(response_payload) + headers = { + "Content-Range": "bytes %d-%d/%d" % (begin, end - 1, length), + "x-goog-hash": revision.x_goog_hash_header(), + "x-goog-generation": revision.generation, + } + if begin == 0: + + def streamer(): + chunk_size = 4 * 1024 + for r in range(0, len(response_payload), chunk_size): + if r >= 256 * 1024: + print("\n\n###### EXIT to simulate crash\n") + sys.exit(1) + time.sleep(0.01) + chunk_end = min(r + chunk_size, len(response_payload)) + yield response_payload[r:chunk_end] + + return flask.Response(streamer(), status=200, headers=headers) + if instructions.endswith("/retry-1"): + print("## Return error for retry 1") + return flask.Response("Service Unavailable", status=503) + if instructions.endswith("/retry-2"): + print("## Return error for retry 2") + return flask.Response("Service Unavailable", status=503) + print("## Return success for %s" % instructions) + return flask.Response(response_payload, status=200, headers=headers) + + response = flask.make_response(response_payload) + length = len(response_payload) + content_range = "bytes %d-%d/%d" % (begin, end - 1, length) + response.headers["Content-Range"] = content_range + response.headers["x-goog-hash"] = revision.x_goog_hash_header() + response.headers["x-goog-generation"] = revision.generation + return response + + +@gcs.route("/b//o/", methods=["DELETE"]) +def objects_delete(bucket_name, object_name): + """Implement the 'Objects: delete' API. Delete objects.""" + object_path, blob = testbench_utils.lookup_object(bucket_name, object_name) + blob.check_preconditions(flask.request) + remove = blob.del_revision(flask.request) + if remove: + testbench_utils.delete_object(object_path) + return testbench_utils.filtered_response(flask.request, {}) + + +@gcs.route("/b//o//compose", methods=["POST"]) +def objects_compose(bucket_name, object_name): + """Implement the 'Objects: compose' API: concatenate Objects.""" + payload = json.loads(flask.request.data) + source_objects = payload["sourceObjects"] + if source_objects is None: + raise error_response.ErrorResponse( + "You must provide at least one source component.", status_code=400 + ) + if len(source_objects) > 32: + raise error_response.ErrorResponse( + "The number of source components provided" + " (%d) exceeds the maximum (32)" % len(source_objects), + status_code=400, + ) + composed_media = b"" + for source_object in source_objects: + source_object_name = source_object.get("name") + if source_object_name is None: + raise error_response.ErrorResponse("Required.", status_code=400) + source_object_path, source_blob = testbench_utils.lookup_object( + bucket_name, source_object_name + ) + source_revision = source_blob.get_latest() + generation = source_object.get("generation") + if generation is not None: + source_revision = source_blob.get_revision_by_generation(generation) + if source_revision is None: + raise error_response.ErrorResponse( + "No such object: %s" % source_object_path, status_code=404 + ) + object_preconditions = source_object.get("objectPreconditions") + if object_preconditions is not None: + if_generation_match = object_preconditions.get("ifGenerationMatch") + source_blob.check_preconditions_by_value( + if_generation_match, None, None, None + ) + composed_media += source_revision.media + composed_object_path, composed_object = testbench_utils.get_object( + bucket_name, object_name, gcs_object.GcsObject(bucket_name, object_name) + ) + composed_object.check_preconditions(flask.request) + base_url = flask.url_for("gcs_index", _external=True) + current_version = composed_object.compose_from( + base_url, flask.request, composed_media + ) + testbench_utils.insert_object(composed_object_path, composed_object) + return testbench_utils.filtered_response(flask.request, current_version.metadata) + + +# Define the WSGI application to handle bucket requests. +DOWNLOAD_HANDLER_PATH = "/download/storage/v1" +download = flask.Flask(__name__) +download.debug = True + + +@download.errorhandler(error_response.ErrorResponse) +def download_error(error): + return error.as_response() + + +@gcs.route("/b//o/") +@download.route("/b//o/") +def objects_get(bucket_name, object_name): + """Implement the 'Objects: get' API. Read objects or their metadata.""" + _, blob = testbench_utils.lookup_object(bucket_name, object_name) + blob.check_preconditions(flask.request) + revision = blob.get_revision(flask.request) + + media = flask.request.args.get("alt", None) + if media is None or media == "json": + return testbench_utils.filtered_response(flask.request, revision.metadata) + if media != "media": + raise error_response.ErrorResponse("Invalid alt=%s parameter" % media) + revision.validate_encryption_for_read(flask.request) + return objects_get_common(bucket_name, object_name, revision) + + +# Define the WSGI application to handle bucket requests. +UPLOAD_HANDLER_PATH = "/upload/storage/v1" +upload = flask.Flask(__name__) +upload.debug = True + + +@upload.errorhandler(error_response.ErrorResponse) +def upload_error(error): + return error.as_response() + + +@upload.route("/b//o", methods=["POST"]) +def objects_insert(bucket_name): + """Implement the 'Objects: insert' API. Insert a new GCS Object.""" + gcs_url = flask.url_for( + "objects_insert", bucket_name=bucket_name, _external=True + ).replace("/upload/", "/") + insert_magic_bucket(gcs_url) + + upload_type = flask.request.args.get("uploadType") + if upload_type is None: + raise error_response.ErrorResponse( + "uploadType not set in Objects: insert", status_code=400 + ) + if upload_type not in {"multipart", "media", "resumable"}: + raise error_response.ErrorResponse( + "testbench does not support %s uploadType" % upload_type, status_code=400 + ) + + if upload_type == "resumable": + bucket = testbench_utils.lookup_bucket(bucket_name) + upload_url = flask.url_for( + "objects_insert", bucket_name=bucket_name, _external=True + ) + return bucket.create_resumable_upload(upload_url, flask.request) + + object_path = None + blob = None + current_version = None + if upload_type == "media": + object_name = flask.request.args.get("name", None) + if object_name is None: + raise error_response.ErrorResponse( + "name not set in Objects: insert", status_code=412 + ) + object_path, blob = testbench_utils.get_object( + bucket_name, object_name, gcs_object.GcsObject(bucket_name, object_name) + ) + blob.check_preconditions(flask.request) + current_version = blob.insert(gcs_url, flask.request) + else: + resource, media_headers, media_body = testbench_utils.parse_multi_part( + flask.request + ) + object_name = flask.request.args.get("name", resource.get("name", None)) + if object_name is None: + raise error_response.ErrorResponse( + "name not set in Objects: insert", status_code=412 + ) + object_path, blob = testbench_utils.get_object( + bucket_name, object_name, gcs_object.GcsObject(bucket_name, object_name) + ) + blob.check_preconditions(flask.request) + current_version = blob.insert_multipart( + gcs_url, flask.request, resource, media_headers, media_body + ) + testbench_utils.insert_object(object_path, blob) + return testbench_utils.filtered_response(flask.request, current_version.metadata) + + +@upload.route("/b//o", methods=["PUT"]) +def resumable_upload_chunk(bucket_name): + """Receive a chunk for a resumable upload.""" + gcs_url = flask.url_for( + "objects_insert", bucket_name=bucket_name, _external=True + ).replace("/upload/", "/") + bucket = testbench_utils.lookup_bucket(bucket_name) + return bucket.receive_upload_chunk(gcs_url, flask.request) + + +@upload.route("/b//o", methods=["DELETE"]) +def delete_resumable_upload(bucket_name): + upload_type = flask.request.args.get("uploadType") + if upload_type != "resumable": + raise error_response.ErrorResponse( + "testbench can delete resumable uploadType only", status_code=400 + ) + upload_id = flask.request.args.get("upload_id") + if upload_id is None: + raise error_response.ErrorResponse( + "missing upload_id in delete_resumable_upload", status_code=400 + ) + bucket = testbench_utils.lookup_bucket(bucket_name) + if upload_id not in bucket.resumable_uploads: + raise error_response.ErrorResponse("upload_id does not exist", status_code=404) + bucket.resumable_uploads.pop(upload_id) + return testbench_utils.filtered_response(flask.request, {}) + + +def xml_put_object(gcs_url, bucket_name, object_name): + """Implement PUT for the XML API.""" + insert_magic_bucket(gcs_url) + object_path, blob = testbench_utils.get_object( + bucket_name, object_name, gcs_object.GcsObject(bucket_name, object_name) + ) + generation_match = flask.request.headers.get("x-goog-if-generation-match") + metageneration_match = flask.request.headers.get("x-goog-if-metageneration-match") + blob.check_preconditions_by_value( + generation_match, None, metageneration_match, None + ) + revision = blob.insert_xml(gcs_url, flask.request) + testbench_utils.insert_object(object_path, blob) + response = flask.make_response("") + response.headers["x-goog-hash"] = revision.x_goog_hash_header() + return response + + +def xml_get_object(bucket_name, object_name): + """Implement the 'Objects: insert' API. Insert a new GCS Object.""" + object_path, blob = testbench_utils.lookup_object(bucket_name, object_name) + if flask.request.args.get("acl") is not None: + raise error_response.ErrorResponse( + "ACL query not supported in XML API", status_code=500 + ) + if flask.request.args.get("encryption") is not None: + raise error_response.ErrorResponse( + "Encryption query not supported in XML API", status_code=500 + ) + generation_match = flask.request.headers.get("if-generation-match") + metageneration_match = flask.request.headers.get("if-metageneration-match") + blob.check_preconditions_by_value( + generation_match, None, metageneration_match, None + ) + revision = blob.get_revision(flask.request) + return objects_get_common(bucket_name, object_name, revision) + + +application = DispatcherMiddleware( + root, + { + GCS_HANDLER_PATH: gcs, + UPLOAD_HANDLER_PATH: upload, + DOWNLOAD_HANDLER_PATH: download, + }, +) diff --git a/tests/test_gcloud/testbench/testbench_utils.py b/tests/test_gcloud/testbench/testbench_utils.py new file mode 100644 index 000000000..8928273c8 --- /dev/null +++ b/tests/test_gcloud/testbench/testbench_utils.py @@ -0,0 +1,324 @@ +# Copyright 2018 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. +"""Standalone helpers for the Google Cloud Storage test bench.""" + +import base64 +import error_response +import hashlib +import json +import random + + +def filter_fields_from_response(fields, response): + """Format the response as a JSON string, using any filtering included in + the request. + + :param fields:str the value of the `fields` parameter in the original + request. + :param response:dict a dictionary to be formatted as a JSON string. + :return: the response formatted as a string. + :rtype:str + """ + if fields is None: + return json.dumps(response) + tmp = {} + for key in fields.split(","): + key.replace(" ", "") + parentheses_idx = key.find("(") + if parentheses_idx != -1: + main_key = key[:parentheses_idx] + child_key = key[parentheses_idx + 1 : -1] + if main_key in response: + children = response[main_key] + if isinstance(children, list): + tmp_list = [] + for value in children: + tmp_list.append(value[child_key]) + tmp[main_key] = tmp_list + elif isinstance(children, dict): + tmp[main_key] = children[child_key] + elif key in response: + tmp[key] = response[key] + return json.dumps(tmp) + + +def filtered_response(request, response): + """Format the response as a JSON string, using any filtering included in + the request. + + :param request:flask.Request the original HTTP request. + :param response:dict a dictionary to be formatted as a JSON string. + :return: the response formatted as a string. + :rtype:str + """ + fields = request.args.get("fields") + return filter_fields_from_response(fields, response) + + +def raise_csek_error(code=400): + msg = "Missing a SHA256 hash of the encryption key, or it is not" + msg += " base64 encoded, or it does not match the encryption key." + link = "https://cloud.google.com/storage/docs/encryption#customer-supplied_encryption_keys" + error = { + "error": { + "errors": [ + { + "domain": "global", + "reason": "customerEncryptionKeySha256IsInvalid", + "message": msg, + "extendedHelp": link, + } + ], + "code": code, + "message": msg, + } + } + raise error_response.ErrorResponse(json.dumps(error), status_code=code) + + +def validate_customer_encryption_headers( + key_header_value, hash_header_value, algo_header_value +): + """Verify that the encryption headers are internally consistent. + + :param key_header_value: str the value of the x-goog-*-key header + :param hash_header_value: str the value of the x-goog-*-key-sha256 header + :param algo_header_value: str the value of the x-goog-*-key-algorithm header + :rtype: NoneType + """ + try: + if algo_header_value is None or algo_header_value != "AES256": + raise error_response.ErrorResponse( + "Invalid or missing algorithm %s for CSEK" % algo_header_value, + status_code=400, + ) + + key = base64.standard_b64decode(key_header_value) + if key is None or len(key) != 256 / 8: + raise_csek_error() + + h = hashlib.sha256() + h.update(key) + expected = base64.standard_b64encode(h.digest()).decode("utf-8") + if hash_header_value is None or expected != hash_header_value: + raise_csek_error() + except error_response.ErrorResponse: + # error_response.ErrorResponse indicates that the request was invalid, just pass + # that exception through. + raise + except Exception: + # Many of the functions above may raise, convert those to an + # error_response.ErrorResponse with the right format. + raise_csek_error() + + +def extract_media(request): + """Extract the media from a flask Request. + + To avoid race conditions when using greenlets we cannot perform I/O in the + constructor of GcsObjectVersion, or in any of the operations that modify + the state of the service. Because sometimes the media is uploaded with + chunked encoding, we need to do I/O before finishing the GcsObjectVersion + creation. If we do this I/O after the GcsObjectVersion creation started, + the the state of the application may change due to other I/O. + + :param request:flask.Request the HTTP request. + :return: the full media of the request. + :rtype: str + """ + if request.environ.get("HTTP_TRANSFER_ENCODING", "") == "chunked": + return request.environ.get("wsgi.input").read() + return request.data + + +def corrupt_media(media): + """Return a randomly modified version of a string. + + :param media:bytes a string (typically some object media) to be modified. + :return: a string that is slightly different than media. + :rtype: str + """ + # Deal with the boundary condition. + if not media: + return bytearray(random.sample("abcdefghijklmnopqrstuvwxyz", 1), "utf-8") + return b"B" + media[1:] if media[0:1] == b"A" else b"A" + media[1:] + + +# Define the collection of Buckets indexed by +GCS_BUCKETS = dict() + + +def lookup_bucket(bucket_name): + """Lookup a bucket by name in the global collection. + + :param bucket_name:str the name of the Bucket. + :return: the bucket matching the name. + :rtype:GcsBucket + :raises:ErrorResponse if the bucket is not found. + """ + bucket = GCS_BUCKETS.get(bucket_name) + if bucket is None: + raise error_response.ErrorResponse( + "Bucket %s not found" % bucket_name, status_code=404 + ) + return bucket + + +def has_bucket(bucket_name): + """Return True if the bucket already exists in the global collection.""" + return GCS_BUCKETS.get(bucket_name) is not None + + +def insert_bucket(bucket_name, bucket): + """Insert (or replace) a new bucket into the global collection. + + :param bucket_name:str the name of the bucket. + :param bucket:GcsBucket the bucket to insert. + """ + GCS_BUCKETS[bucket_name] = bucket + + +def delete_bucket(bucket_name): + """Delete a bucket from the global collection.""" + GCS_BUCKETS.pop(bucket_name) + + +def all_buckets(): + """Return a key,value iterator for all the buckets in the global collection. + + :rtype:dict[str, GcsBucket] + """ + return GCS_BUCKETS.items() + + +# Define the collection of GcsObjects indexed by /o/ +GCS_OBJECTS = dict() + + +def lookup_object(bucket_name, object_name): + """Lookup an object by name in the global collection. + + :param bucket_name:str the name of the Bucket that contains the object. + :param object_name:str the name of the Object. + :return: tuple the object path and the object. + :rtype: (str,GcsObject) + :raises:ErrorResponse if the object is not found. + """ + object_path, gcs_object = get_object(bucket_name, object_name, None) + if gcs_object is None: + raise error_response.ErrorResponse( + "Object {} in {} not found".format(object_name, bucket_name), + status_code=404, + ) + return object_path, gcs_object + + +def get_object(bucket_name, object_name, default_value): + """Find an object in the global collection, return a default value if not + found. + + :param bucket_name:str the name of the Bucket that contains the object. + :param object_name:str the name of the Object. + :param default_value:GcsObject the default value returned if the object is + not found. + :return: tuple the object path and the object. + :rtype: (str,GcsObject) + """ + object_path = bucket_name + "/o/" + object_name + return object_path, GCS_OBJECTS.get(object_path, default_value) + + +def insert_object(object_path, value): + """Insert an object to the global collection.""" + GCS_OBJECTS[object_path] = value + + +def delete_object(object_path): + """Delete an object from the global collection.""" + GCS_OBJECTS.pop(object_path) + + +def all_objects(): + """Return a key,value iterator for all the objects in the global collection. + + :rtype:dict[str, GcsBucket] + """ + return GCS_OBJECTS.items() + + +def parse_multi_part(request): + """Parse a multi-part request + + :param request:flask.Request multipart request. + :return: a tuple with the resource, media_headers and the media_body. + :rtype: (dict, dict, str) + """ + content_type = request.headers.get("content-type") + if content_type is None or not content_type.startswith("multipart/related"): + raise error_response.ErrorResponse( + "Missing or invalid content-type header in multipart upload" + ) + _, _, boundary = content_type.partition("boundary=") + boundary = boundary.strip('"') + if boundary is None: + raise error_response.ErrorResponse( + "Missing or invalid boundary in content-type header in multipart upload" + ) + + def parse_metadata(part): + result = part.split(b"\r\n") + if result[0] != b"" and result[-1] != b"": + raise error_response.ErrorResponse( + "Missing or invalid multipart %s" % str(part) + ) + result = list(filter(None, result)) + headers = {} + if len(result) < 2: + result.append(b"") + for header in result[:-1]: + key, value = header.split(b": ") + headers[key.decode("utf-8").lower()] = value.decode("utf-8") + return result[-1] + + def parse_body(part): + if part[0:2] != b"\r\n" or part[-2:] != b"\r\n": + raise error_response.ErrorResponse( + "Missing or invalid multipart %s" % str(part) + ) + part = part[2:-2] + part.lstrip(b"\r\n") + content_type_index = part.find(b"\r\n") + if content_type_index == -1: + raise error_response.ErrorResponse( + "Missing or invalid multipart %s" % str(part) + ) + content_type = part[:content_type_index] + _, value = content_type.decode("utf-8").split(": ") + media = part[content_type_index + 2 :] + if media[:2] == b"\r\n": + # It is either `\r\n` or `\r\n\r\n`, we should remove at most 4 characters. + media = media[2:] + return {"content-type": value}, media + + boundary = boundary.encode("utf-8") + body = extract_media(request) + parts = body.split(b"--" + boundary) + if parts[-1] != b"--\r\n" and parts[-1] != b"--": + raise error_response.ErrorResponse( + "Missing end marker (--%s--) in media body" % boundary + ) + resource = parse_metadata(parts[1]) + metadata = json.loads(resource) + content_type, media = parse_body(parts[2]) + return metadata, content_type, media diff --git a/tests/test_gcs_eager.py b/tests/test_gcs_eager.py index 84556d354..9939a775d 100644 --- a/tests/test_gcs_eager.py +++ b/tests/test_gcs_eager.py @@ -43,19 +43,18 @@ def test_read_file(): body = b"1234567" - # Setup the S3 bucket and key + # Setup the GCS bucket and key key_name = "TEST" - bucket_name = "s3e{}e".format(int(time.time())) - + bucket_name = "gse{}e".format(int(time.time())) bucket = client.create_bucket(bucket_name) - print("Project number: {}".format(bucket.project_number)) blob = bucket.blob(key_name) blob.upload_from_string(body) - response = blob.download_as_string() - print("RESPONSE: ", response) + response = blob.download_as_bytes() assert response == body - # content = tf.io.read_file("gs://{}/{}".format(bucket_name, key_name)) - # assert content == body + os.environ["CLOUD_STORAGE_TESTBENCH_ENDPOINT"] = "http://localhost:9099" + + content = tf.io.read_file("gse://{}/{}".format(bucket_name, key_name)) + assert content == body diff --git a/third_party/curl.BUILD b/third_party/curl.BUILD index 0bcbee9d1..6d4d40fa2 100644 --- a/third_party/curl.BUILD +++ b/third_party/curl.BUILD @@ -14,7 +14,9 @@ cc_library( "lib/vtls/*.h", "lib/vauth/*.h", "lib/vauth/*.c", - ]), + ]) + [ + "lib/vssh/ssh.h", + ], hdrs = glob([ "include/curl/*.h", ]) + [ From 04d6913b85a78d229fc6b541cd157c2852ea2c00 Mon Sep 17 00:00:00 2001 From: Vo Van Nghia Date: Fri, 18 Dec 2020 21:34:07 +0700 Subject: [PATCH 12/85] fix nightly build because of missing `google-cloud-storage` (#1238) --- .github/workflows/api.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/api.yml b/.github/workflows/api.yml index fe43d4e16..dd80f4165 100644 --- a/.github/workflows/api.yml +++ b/.github/workflows/api.yml @@ -43,7 +43,7 @@ jobs: rm -rf tensorflow_io echo ${{ matrix.version }} | awk -F: '{print $1}' | xargs python -m pip install -U echo ${{ matrix.version }} | awk -F: '{print $2}' | xargs python -m pip install --no-deps -U - python -m pip install pytest-benchmark boto3 + python -m pip install pytest-benchmark boto3 google-cloud-storage==1.32.0 python -m pip freeze python -c 'import tensorflow as tf; print(tf.version.VERSION)' python -c 'import tensorflow_io as tfio; print(tfio.version.VERSION)' @@ -79,7 +79,7 @@ jobs: rm -rf tensorflow_io echo ${{ matrix.version }} | awk -F: '{print $1}' | xargs python -m pip install -U echo ${{ matrix.version }} | awk -F: '{print $2}' | xargs python -m pip install --no-deps -U - python -m pip install pytest-benchmark boto3 + python -m pip install pytest-benchmark boto3 google-cloud-storage==1.32.0 python -m pip freeze python -c 'import tensorflow as tf; print(tf.version.VERSION)' python -c 'import tensorflow_io as tfio; print(tfio.version.VERSION)' From aa1a95d98e1a62249a31d6ac4f8f1bd8f284e152 Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Fri, 18 Dec 2020 06:59:54 -0800 Subject: [PATCH 13/85] Remove the CI build for CentOS 8 (#1237) Building shared libraries on CentOS 8 is pretty much the same as on Ubuntu 20.04 except `apt` should be changed to `yum`. For that our CentOS 8 CI test is not adding a lot of value. Furthermore with the upcoming CentOS 8 change: https://www.phoronix.com/scan.php?page=news_item&px=CentOS-8-Ending-For-Stream CentOS 8 is effectively EOLed at 2021. For that we may want to drop the CentOS 8 build (only leave a comment in README.md) Note we keep CentOS 7 build for now as there are still many users using CentOS 7 and CentOS 7 will only be EOLed at 2024. We might drop CentOS 7 build in the future as well if there is similiar changes to CentOS 7 like CentOS 8. Signed-off-by: Yong Tang --- .github/workflows/build.yml | 13 ------------- README.md | 29 ++++------------------------- 2 files changed, 4 insertions(+), 38 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fbe84c670..dc3700ab3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -72,19 +72,6 @@ jobs: docker run -i --rm -v $PWD:/v -w /v --net=host ubuntu:20.04 \ bash -x -e source.sh - centos-8: - name: CentOS 8 - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - run: | - set -x -e - bash -x -e .github/workflows/build.space.sh - python3 .github/workflows/build.instruction.py README.md "##### CentOS 8" > source.sh - cat source.sh - docker run -i --rm -v $PWD:/v -w /v --net=host centos:8 \ - bash -x -e source.sh - centos-7: name: CentOS 7 runs-on: ubuntu-latest diff --git a/README.md b/README.md index 321ed0139..7c339ef65 100644 --- a/README.md +++ b/README.md @@ -277,33 +277,12 @@ TFIO_DATAPATH=bazel-bin python3 -m pytest -s -v tests/test_serialization_eager.p ##### CentOS 8 -CentOS 8 requires gcc/g++, git, and python 3. The following will install dependencies and build -the shared libraries on CentOS 8: -```sh -#!/usr/bin/env bash - -# Install gcc/g++, git, unzip/which (for bazel), and python3 +The steps to build shared libraries for CentOS 8 is similiar to Ubuntu 20.04 above +excpet that +``` sudo yum install -y python3 python3-devel gcc gcc-c++ git unzip which make - -# Install Bazel version specified in .bazelversion -curl -sSOL https://github.com/bazelbuild/bazel/releases/download/$(cat .bazelversion)/bazel-$(cat .bazelversion)-installer-linux-x86_64.sh -sudo bash -x -e bazel-$(cat .bazelversion)-installer-linux-x86_64.sh - -# Upgrade pip -sudo python3 -m pip install -U pip - -# Install tensorflow and configure bazel -sudo ./configure.sh - -# Build shared libraries -bazel build -s --verbose_failures //tensorflow_io/... - -# Once build is complete, shared libraries will be available in -# `bazel-bin/tensorflow_io/core/python/ops/` and it is possible -# to run tests with `pytest`, e.g.: -sudo python3 -m pip install pytest -TFIO_DATAPATH=bazel-bin python3 -m pytest -s -v tests/test_serialization_eager.py ``` +should be used instead to install gcc/g++, git, unzip/which (for bazel), and python3. ##### CentOS 7 From 6c298131d8b6a5326304b74322fd5a837447187c Mon Sep 17 00:00:00 2001 From: Vignesh Kothapalli Date: Tue, 22 Dec 2020 20:56:49 +0530 Subject: [PATCH 14/85] [MongoDB] update API docstrings (#1243) * [mongoDB] update API docs * lint fixes * rename wrong API * lint fixes --- .../experimental/mongodb_dataset_ops.py | 47 ++++++++++++++++++- .../python/experimental/mongodb_writer_ops.py | 45 +++++++++++++++++- 2 files changed, 90 insertions(+), 2 deletions(-) diff --git a/tensorflow_io/core/python/experimental/mongodb_dataset_ops.py b/tensorflow_io/core/python/experimental/mongodb_dataset_ops.py index 7f0b963f3..7331db65f 100644 --- a/tensorflow_io/core/python/experimental/mongodb_dataset_ops.py +++ b/tensorflow_io/core/python/experimental/mongodb_dataset_ops.py @@ -54,9 +54,54 @@ def get_next_batch(self, resource): class MongoDBIODataset(tf.data.Dataset): - """Fetch records from mongoDB""" + """Fetch records from mongoDB + + The dataset aids in faster retrieval of data from MongoDB collections. + + To make a connection and read the documents from the mongo collections, + the `tfio.experimental.mongodb.MongoDBIODataset` API can be used. + + Example: + + >>> URI = "mongodb://mongoadmin:default_password@localhost:27017" + >>> DATABASE = "tfiodb" + >>> COLLECTION = "test" + >>> dataset = tfio.experimental.mongodb.MongoDBIODataset( + uri=URI, database=DATABASE, collection=COLLECTION) + + Perform operations on the dataset as one would with any `tf.data.Dataset` + >>> dataset = dataset.map(transform_func) + >>> dataset = dataset.batch(batch_size) + + Assuming the user has already built a `tf.keras` model, the dataset can be directly + passed for training purposes. + + >>> model.fit(dataset) # to train + >>> model.predict(dataset) # to infer + + """ def __init__(self, uri, database, collection): + """Initialize the dataset with the following parameters + + Args: + uri: The uri of the mongo server or replicaset to connect to. + - To connect to a MongoDB server with username and password + based authentication, the following uri pattern can be used. + Example: `"mongodb://mongoadmin:default_password@localhost:27017"`. + + - Connecting to a replica set is much like connecting to a + standalone MongoDB server. Simply specify the replica set name + using the `?replicaSet=myreplset` URI option. + Example: "mongodb://host01:27017,host02:27017,host03:27017/?replicaSet=myreplset" + + Additional information on writing uri's can be found here: + - [libmongoc uri docs](http://mongoc.org/libmongoc/current/mongoc_uri_t.html) + - [mongodb uri docs](https://docs.mongodb.com/manual/reference/connection-string/) + database: The database in the standalone standalone MongoDB server or a replica set + to connect to. + collection: The collection from which the documents have to be retrieved. + """ handler = _MongoDBHandler(uri=uri, database=database, collection=collection) resource = handler.get_healthy_resource() dataset = tf.data.experimental.Counter() diff --git a/tensorflow_io/core/python/experimental/mongodb_writer_ops.py b/tensorflow_io/core/python/experimental/mongodb_writer_ops.py index 3af8a14bf..cb1fff29e 100644 --- a/tensorflow_io/core/python/experimental/mongodb_writer_ops.py +++ b/tensorflow_io/core/python/experimental/mongodb_writer_ops.py @@ -22,10 +22,53 @@ class MongoDBWriter: - """Write documents to mongoDB""" + """Write documents to mongoDB. + + The writer can be used to store documents in mongoDB while dealing with tensorflow + based models and inference outputs. Without loss of generality, consider an ML + model that is being used for inference. The outputs of inference can be modelled into + a structured record by enriching the schema with additional information( for ex: metadata + about input data and the semantics of the inference etc.) and can be stored in mongo + collections for persistence or future analysis. + + To make a connection and write the documents to the mongo collections, + the `tfio.experimental.mongodb.MongoDBWriter` API can be used. + + Example: + + >>> URI = "mongodb://mongoadmin:default_password@localhost:27017" + >>> DATABASE = "tfiodb" + >>> COLLECTION = "test" + >>> writer = tfio.experimental.mongodb.MongoDBWriter( + uri=URI, database=DATABASE, collection=COLLECTION + ) + >>> for i in range(1000): + ... data = {"key{}".format(i): "value{}".format(i)} + ... writer.write(data) + + """ def __init__(self, uri, database, collection): + """Initialize the dataset with the following parameters + + Args: + uri: The uri of the mongo server or replicaset to connect to. + - To connect to a MongoDB server with username and password + based authentication, the following uri pattern can be used. + Example: `"mongodb://mongoadmin:default_password@localhost:27017"`. + + - Connecting to a replica set is much like connecting to a + standalone MongoDB server. Simply specify the replica set name + using the `?replicaSet=myreplset` URI option. + Example: "mongodb://host01:27017,host02:27017,host03:27017/?replicaSet=myreplset" + Additional information on writing uri's can be found here: + - [libmongoc uri docs](http://mongoc.org/libmongoc/current/mongoc_uri_t.html) + - [mongodb uri docs](https://docs.mongodb.com/manual/reference/connection-string/) + database: The database in the standalone standalone MongoDB server or a replica set + to connect to. + collection: The collection from which the documents have to be retrieved. + """ self.uri = uri self.database = database self.collection = collection From 371877e910eb157c10129ca6a0d91772148137fe Mon Sep 17 00:00:00 2001 From: Yuan Tang Date: Tue, 22 Dec 2020 11:27:17 -0500 Subject: [PATCH 15/85] Remove redundant output of dataset.element_spec in PostgreSQL tutorial (#1242) --- docs/tutorials/postgresql.ipynb | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/docs/tutorials/postgresql.ipynb b/docs/tutorials/postgresql.ipynb index b5b3d7f35..c5d9426b3 100644 --- a/docs/tutorials/postgresql.ipynb +++ b/docs/tutorials/postgresql.ipynb @@ -275,14 +275,7 @@ "id": "8y-VpwcWNYTF" }, "source": [ - "As you could see from the output of `dataset.element_spec` above, the element of the created `Dataset` is a python dict object with column names of the database table as keys:\n", - "```\n", - "{\n", - " 'co': TensorSpec(shape=(), dtype=tf.float32, name=None),\n", - " 'pt08s1': TensorSpec(shape=(), dtype=tf.int32, name=None),\n", - "}\n", - "```\n", - "\n", + "As you could see from the output of `dataset.element_spec` above, the element of the created `Dataset` is a python dict object with column names of the database table as keys.\n", "It is quite convenient to apply further operations. For example, you could select both `nox` and `no2` field of the `Dataset`, and calculate the difference:" ] }, From 88b9d8d6681c130406ca6cfd16c3fa8b2bb910ff Mon Sep 17 00:00:00 2001 From: Vo Van Nghia Date: Wed, 23 Dec 2020 01:30:52 +0700 Subject: [PATCH 16/85] add tf-c-header rule (#1244) --- tensorflow_io/core/plugins/BUILD | 2 +- tensorflow_io/core/plugins/hdfs/BUILD | 2 ++ third_party/toolchains/tf/BUILD.tpl | 11 ++++++++++- third_party/toolchains/tf/tf_configure.bzl | 7 +++++++ 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/tensorflow_io/core/plugins/BUILD b/tensorflow_io/core/plugins/BUILD index 6559a6ba6..d6b2d83c2 100644 --- a/tensorflow_io/core/plugins/BUILD +++ b/tensorflow_io/core/plugins/BUILD @@ -16,7 +16,7 @@ cc_library( linkstatic = True, deps = [ "@local_config_tf//:libtensorflow_framework", - "@local_config_tf//:tf_header_lib", + "@local_config_tf//:tf_c_header_lib", ], alwayslink = 1, ) diff --git a/tensorflow_io/core/plugins/hdfs/BUILD b/tensorflow_io/core/plugins/hdfs/BUILD index d4571712f..867310815 100644 --- a/tensorflow_io/core/plugins/hdfs/BUILD +++ b/tensorflow_io/core/plugins/hdfs/BUILD @@ -16,6 +16,8 @@ cc_library( linkstatic = True, deps = [ "//tensorflow_io/core/plugins:plugins_header", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/synchronization", "@hadoop", ], alwayslink = 1, diff --git a/third_party/toolchains/tf/BUILD.tpl b/third_party/toolchains/tf/BUILD.tpl index bee021f10..80a428bc3 100644 --- a/third_party/toolchains/tf/BUILD.tpl +++ b/third_party/toolchains/tf/BUILD.tpl @@ -7,6 +7,14 @@ cc_library( visibility = ["//visibility:public"], ) +cc_library( + name = "tf_c_header_lib", + hdrs = [":tf_c_header_include"], + include_prefix = "tensorflow/c", + strip_include_prefix = "include_c", + visibility = ["//visibility:public"], +) + cc_library( name = "libtensorflow_framework", srcs = [":libtensorflow_framework.so"], @@ -15,4 +23,5 @@ cc_library( ) %{TF_HEADER_GENRULE} -%{TF_SHARED_LIBRARY_GENRULE} \ No newline at end of file +%{TF_C_HEADER_GENRULE} +%{TF_SHARED_LIBRARY_GENRULE} diff --git a/third_party/toolchains/tf/tf_configure.bzl b/third_party/toolchains/tf/tf_configure.bzl index 18e344388..b898511bf 100644 --- a/third_party/toolchains/tf/tf_configure.bzl +++ b/third_party/toolchains/tf/tf_configure.bzl @@ -176,6 +176,12 @@ def _tf_pip_impl(repository_ctx): "tf_header_include", tf_pip_dir_rename_pair = ["tensorflow_core", "tensorflow"], ) + tf_c_header_rule = _symlink_genrule_for_dir( + repository_ctx, + tf_header_dir + "/tensorflow/c/", + "include_c", + "tf_c_header_include", + ) tf_shared_library_dir = repository_ctx.os.environ[_TF_SHARED_LIBRARY_DIR] tf_shared_library_name = repository_ctx.os.environ[_TF_SHARED_LIBRARY_NAME] @@ -192,6 +198,7 @@ def _tf_pip_impl(repository_ctx): _tpl(repository_ctx, "BUILD", { "%{TF_HEADER_GENRULE}": tf_header_rule, + "%{TF_C_HEADER_GENRULE}": tf_c_header_rule, "%{TF_SHARED_LIBRARY_GENRULE}": tf_shared_library_rule, }) From d695644f0d662dba23d003f88b02af02ffb52501 Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Tue, 22 Dec 2020 18:47:12 -0800 Subject: [PATCH 17/85] Add `fail-fast: false` to API Compatibility GitHub Actions (#1246) This PR adds `fail-fast: false` to API Compatibility GitHub Actions. The main reason is to make sure if any job fails, the parallel jobs within the same matrix of the workflow can continue. The API Compatibility is to see how our plugin binaries match with different versions and as such we want to see the whole compatibility match up results. Signed-off-by: Yong Tang --- .github/workflows/api.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/api.yml b/.github/workflows/api.yml index dd80f4165..dce3802ce 100644 --- a/.github/workflows/api.yml +++ b/.github/workflows/api.yml @@ -16,6 +16,7 @@ jobs: name: macOS ${{ matrix.python }} + ${{ matrix.version }} runs-on: macos-latest strategy: + fail-fast: false matrix: python: ['3.8'] version: ['tensorflow==2.4.0:tensorflow-io-nightly', 'tf-nightly:tensorflow-io==0.17.0', 'tf-nightly:tensorflow-io-nightly'] @@ -56,6 +57,7 @@ jobs: name: Linux ${{ matrix.python }} + ${{ matrix.version }} runs-on: ubuntu-20.04 strategy: + fail-fast: false matrix: python: ['3.8'] version: ['tensorflow==2.4.0:tensorflow-io-nightly', 'tf-nightly:tensorflow-io==0.17.0', 'tf-nightly:tensorflow-io-nightly'] @@ -92,6 +94,7 @@ jobs: name: Windows ${{ matrix.python }} + ${{ matrix.version }} runs-on: windows-latest strategy: + fail-fast: false matrix: python: ['3.8'] version: ['tensorflow==2.4.0:tensorflow-io-nightly', 'tf-nightly:tensorflow-io==0.17.0', 'tf-nightly:tensorflow-io-nightly'] From b0ffa2eddcea144ea78aecaa299ab2402443a31e Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Tue, 22 Dec 2020 23:08:48 -0800 Subject: [PATCH 18/85] Skip tf-nightly:tensorflow-io==0.17.0 on API compatibility test (#1247) Signed-off-by: Yong Tang --- .github/workflows/api.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/api.yml b/.github/workflows/api.yml index dce3802ce..4c61f586b 100644 --- a/.github/workflows/api.yml +++ b/.github/workflows/api.yml @@ -88,7 +88,7 @@ jobs: python -m pytest -s -v tests/test_http_eager.py python -m pytest -s -v tests/test_s3_eager.py python -m pytest -s -v tests/test_azure.py - python -m pytest -s -v tests/test_gcs_eager.py + if [[ "${{ matrix.version }}" != "tf-nightly:tensorflow-io==0.17.0" ]]; then python -m pytest -s -v tests/test_gcs_eager.py ; fi windows: name: Windows ${{ matrix.python }} + ${{ matrix.version }} From a68633e142ced811a070f0dc1e11dd6aa9e2a334 Mon Sep 17 00:00:00 2001 From: Vo Van Nghia Date: Thu, 24 Dec 2020 01:42:51 +0700 Subject: [PATCH 19/85] S3 Improvements (#1248) * fix `curl ssl` BUILD on macos * split `@aws-sdk-cpp` to smaller components * More http code support for `TF_SetStatusFromAWSError` * Fix memory problem with `s3` * s3: `RecursivelyCreateDir` fallback to `CreateDir` --- tensorflow_io/core/BUILD | 2 +- tensorflow_io/core/plugins/s3/BUILD | 3 +- .../core/plugins/s3/s3_filesystem.cc | 105 +++++++----- third_party/aws-sdk-cpp.BUILD | 160 ++++++++++++------ third_party/curl.BUILD | 2 +- 5 files changed, 178 insertions(+), 94 deletions(-) diff --git a/tensorflow_io/core/BUILD b/tensorflow_io/core/BUILD index 87f5b68cc..184af811d 100644 --- a/tensorflow_io/core/BUILD +++ b/tensorflow_io/core/BUILD @@ -463,7 +463,7 @@ cc_library( linkstatic = True, deps = [ "//tensorflow_io/core:dataset_ops", - "@aws-sdk-cpp", + "@aws-sdk-cpp//:kinesis", ], alwayslink = 1, ) diff --git a/tensorflow_io/core/plugins/s3/BUILD b/tensorflow_io/core/plugins/s3/BUILD index 6007d4d0d..7b262c053 100644 --- a/tensorflow_io/core/plugins/s3/BUILD +++ b/tensorflow_io/core/plugins/s3/BUILD @@ -21,7 +21,8 @@ cc_library( linkstatic = True, deps = [ "//tensorflow_io/core/plugins:plugins_header", - "@aws-sdk-cpp", + "@aws-sdk-cpp//:s3", + "@aws-sdk-cpp//:transfer", "@com_google_absl//absl/strings", "@com_google_absl//absl/synchronization", ], diff --git a/tensorflow_io/core/plugins/s3/s3_filesystem.cc b/tensorflow_io/core/plugins/s3/s3_filesystem.cc index 7bc5dd41d..b1b1736f5 100644 --- a/tensorflow_io/core/plugins/s3/s3_filesystem.cc +++ b/tensorflow_io/core/plugins/s3/s3_filesystem.cc @@ -62,29 +62,41 @@ constexpr size_t kUploadRetries = 3; constexpr size_t kS3ReadAppendableFileBufferSize = 1024 * 1024; // 1 MB -static void* plugin_memory_allocate(size_t size) { return calloc(1, size); } -static void plugin_memory_free(void* ptr) { free(ptr); } - static inline void TF_SetStatusFromAWSError( const Aws::Client::AWSError& error, TF_Status* status) { - switch (error.GetResponseCode()) { - case Aws::Http::HttpResponseCode::FORBIDDEN: - TF_SetStatus(status, TF_FAILED_PRECONDITION, - "AWS Credentials have not been set properly. " - "Unable to access the specified S3 location"); - break; - case Aws::Http::HttpResponseCode::REQUESTED_RANGE_NOT_SATISFIABLE: - TF_SetStatus(status, TF_OUT_OF_RANGE, "Read less bytes than requested"); - break; - case Aws::Http::HttpResponseCode::NOT_FOUND: - TF_SetStatus(status, TF_NOT_FOUND, error.GetMessage().c_str()); - break; - default: - TF_SetStatus( - status, TF_UNKNOWN, - (error.GetExceptionName() + ": " + error.GetMessage()).c_str()); - break; + auto http_code = error.GetResponseCode(); + auto status_msg = error.GetExceptionName() + ": " + error.GetMessage(); + if (http_code == Aws::Http::HttpResponseCode::BAD_REQUEST) { + return TF_SetStatus(status, TF_INVALID_ARGUMENT, status_msg.c_str()); + } + if (http_code == Aws::Http::HttpResponseCode::UNAUTHORIZED) { + return TF_SetStatus(status, TF_UNAUTHENTICATED, status_msg.c_str()); + } + if (http_code == Aws::Http::HttpResponseCode::FORBIDDEN) { + return TF_SetStatus(status, TF_PERMISSION_DENIED, status_msg.c_str()); + } + if (http_code == Aws::Http::HttpResponseCode::NOT_FOUND) { + return TF_SetStatus(status, TF_NOT_FOUND, status_msg.c_str()); + } + if (http_code == Aws::Http::HttpResponseCode::METHOD_NOT_ALLOWED || + http_code == Aws::Http::HttpResponseCode::NOT_ACCEPTABLE || + http_code == Aws::Http::HttpResponseCode::PROXY_AUTHENTICATION_REQUIRED) { + return TF_SetStatus(status, TF_PERMISSION_DENIED, status_msg.c_str()); + } + if (http_code == Aws::Http::HttpResponseCode::REQUEST_TIMEOUT) { + return TF_SetStatus(status, TF_RESOURCE_EXHAUSTED, status_msg.c_str()); + } + if (http_code == Aws::Http::HttpResponseCode::PRECONDITION_FAILED) { + return TF_SetStatus(status, TF_FAILED_PRECONDITION, status_msg.c_str()); } + if (http_code == + Aws::Http::HttpResponseCode::REQUESTED_RANGE_NOT_SATISFIABLE) { + return TF_SetStatus(status, TF_OUT_OF_RANGE, status_msg.c_str()); + } + if (Aws::Http::HttpResponseCode::INTERNAL_SERVER_ERROR <= http_code) { + return TF_SetStatus(status, TF_INTERNAL, status_msg.c_str()); + } + return TF_SetStatus(status, TF_UNKNOWN, status_msg.c_str()); } void ParseS3Path(const Aws::String& fname, bool object_empty_ok, @@ -218,9 +230,17 @@ static void GetS3Client(tf_s3_filesystem::S3File* s3_file) { // in the bucket name. Due to TLS hostname validation or DNS rules, // the bucket may not be resolved. Disabling of virtual addressing // should address the issue. See GitHub issue 16397 for details. - s3_file->s3_client = Aws::MakeShared( - kS3ClientAllocationTag, GetDefaultClientConfig(), - Aws::Client::AWSAuthV4Signer::PayloadSigningPolicy::Never, false); + s3_file->s3_client = std::shared_ptr( + Aws::New( + kS3ClientAllocationTag, GetDefaultClientConfig(), + Aws::Client::AWSAuthV4Signer::PayloadSigningPolicy::Never, false), + [&options](Aws::S3::S3Client* s3_client) { + if (s3_client != nullptr) { + Aws::Delete(s3_client); + Aws::ShutdownAPI(options); + tf_s3_filesystem::AWSLogSystem::ShutdownAWSLogging(); + } + }); } } @@ -255,15 +275,6 @@ static void GetTransferManager( } } -static void ShutdownClient(Aws::S3::S3Client* s3_client) { - if (s3_client != nullptr) { - delete s3_client; - Aws::SDKOptions options; - Aws::ShutdownAPI(options); - tf_s3_filesystem::AWSLogSystem::ShutdownAWSLogging(); - } -} - // SECTION 1. Implementation for `TF_RandomAccessFile` // ---------------------------------------------------------------------------- namespace tf_random_access_file { @@ -319,11 +330,10 @@ static int64_t ReadS3Client(S3File* s3_file, uint64_t offset, size_t n, static int64_t ReadS3TransferManager(S3File* s3_file, uint64_t offset, size_t n, char* buffer, TF_Status* status) { TF_VLog(3, "Using TransferManager\n"); - auto create_download_stream = [&]() { - return Aws::New( - "S3ReadStream", - Aws::New( - "S3ReadStream", reinterpret_cast(buffer), n)); + auto stream_buf = Aws::MakeShared( + "S3StreamBuf", reinterpret_cast(buffer), n); + auto create_download_stream = [stream_buf]() { + return Aws::New("S3ReadStream", stream_buf.get()); }; TF_VLog(3, "Created stream to read with transferManager\n"); auto handle = s3_file->transfer_manager->DownloadFile( @@ -439,9 +449,11 @@ void Sync(const TF_WritableFile* file, TF_Status* status) { TF_VLog(1, "WriteFileToS3: s3://%s/%s\n", s3_file->bucket.c_str(), s3_file->object.c_str()); auto position = static_cast(s3_file->outfile->tellp()); + // We always re-upload the whole file. + s3_file->outfile->seekg(0); auto handle = s3_file->transfer_manager->UploadFile( s3_file->outfile, s3_file->bucket, s3_file->object, - "application/octet-stream", Aws::Map()); + "application/octet-stream", /*metadata*/ {}); handle->WaitUntilFinished(); size_t retries = 0; @@ -512,7 +524,7 @@ uint64_t Length(const TF_ReadOnlyMemoryRegion* region) { // is an error - should return OUT_OF_RANGE with less bytes. namespace tf_s3_filesystem { S3File::S3File() - : s3_client(nullptr, ShutdownClient), + : s3_client(nullptr), executor(nullptr), transfer_managers(), multi_part_chunk_sizes(), @@ -768,8 +780,8 @@ static void SimpleCopyFile(const Aws::String& source, const Aws::String& bucket_dst, const Aws::String& object_dst, S3File* s3_file, TF_Status* status) { - TF_VLog(1, "SimpleCopyFile from %s to %s/%s\n", bucket_dst.c_str(), - object_dst.c_str()); + TF_VLog(1, "SimpleCopyFile from %s to %s/%s\n", source.c_str(), + bucket_dst.c_str(), object_dst.c_str()); Aws::S3::Model::CopyObjectRequest copy_object_request; copy_object_request.WithCopySource(source) .WithBucket(bucket_dst) @@ -834,8 +846,8 @@ static void MultiPartCopy(const Aws::String& source, const Aws::String& object_dst, const size_t num_parts, const uint64_t file_size, S3File* s3_file, TF_Status* status) { - TF_VLog(1, "MultiPartCopy from %s to %s/%s\n", bucket_dst.c_str(), - object_dst.c_str()); + TF_VLog(1, "MultiPartCopy from %s to %s/%s\n", source.c_str(), + bucket_dst.c_str(), object_dst.c_str()); Aws::S3::Model::CreateMultipartUploadRequest create_multipart_upload_request; create_multipart_upload_request.WithBucket(bucket_dst).WithKey(object_dst); @@ -1066,6 +1078,11 @@ void CreateDir(const TF_Filesystem* filesystem, const char* path, TF_SetStatus(status, TF_OK, ""); } +void RecursivelyCreateDir(const TF_Filesystem* filesystem, const char* path, + TF_Status* status) { + CreateDir(filesystem, path, status); +} + void DeleteDir(const TF_Filesystem* filesystem, const char* path, TF_Status* status) { TF_VLog(1, "DeleteDir: %s\n", path); @@ -1262,6 +1279,8 @@ void ProvideFilesystemSupportFor(TF_FilesystemPluginOps* ops, const char* uri) { ops->filesystem_ops->new_read_only_memory_region_from_file = tf_s3_filesystem::NewReadOnlyMemoryRegionFromFile; ops->filesystem_ops->create_dir = tf_s3_filesystem::CreateDir; + ops->filesystem_ops->recursively_create_dir = + tf_s3_filesystem::RecursivelyCreateDir; ops->filesystem_ops->delete_file = tf_s3_filesystem::DeleteFile; ops->filesystem_ops->delete_dir = tf_s3_filesystem::DeleteDir; ops->filesystem_ops->copy_file = tf_s3_filesystem::CopyFile; diff --git a/third_party/aws-sdk-cpp.BUILD b/third_party/aws-sdk-cpp.BUILD index 636d50c95..cd939724e 100644 --- a/third_party/aws-sdk-cpp.BUILD +++ b/third_party/aws-sdk-cpp.BUILD @@ -8,51 +8,73 @@ licenses(["notice"]) # Apache 2.0 exports_files(["LICENSE"]) cc_library( - name = "aws-sdk-cpp", + name = "core", srcs = glob([ - "aws-cpp-sdk-core/include/**/*.h", - "aws-cpp-sdk-core/source/*.cpp", - "aws-cpp-sdk-core/source/auth/**/*.cpp", - "aws-cpp-sdk-core/source/client/**/*.cpp", - "aws-cpp-sdk-core/source/config/**/*.cpp", - "aws-cpp-sdk-core/source/external/**/*.cpp", - "aws-cpp-sdk-core/source/http/*.cpp", - "aws-cpp-sdk-core/source/http/curl/*.cpp", - "aws-cpp-sdk-core/source/http/standard/*.cpp", - "aws-cpp-sdk-core/source/internal/**/*.cpp", - "aws-cpp-sdk-core/source/monitoring/**/*.cpp", - "aws-cpp-sdk-core/source/utils/*.cpp", - "aws-cpp-sdk-core/source/utils/base64/**/*.cpp", - "aws-cpp-sdk-core/source/utils/crypto/*.cpp", - "aws-cpp-sdk-core/source/utils/crypto/factory/*.cpp", - "aws-cpp-sdk-core/source/utils/crypto/openssl/CryptoImpl.cpp", - "aws-cpp-sdk-core/source/utils/event/**/*.cpp", - "aws-cpp-sdk-core/source/utils/json/**/*.cpp", - "aws-cpp-sdk-core/source/utils/logging/**/*.cpp", - "aws-cpp-sdk-core/source/utils/memory/**/*.cpp", - "aws-cpp-sdk-core/source/utils/stream/**/*.cpp", - "aws-cpp-sdk-core/source/utils/threading/**/*.cpp", - "aws-cpp-sdk-core/source/utils/xml/**/*.cpp", - "aws-cpp-sdk-kinesis/include/**/*.h", - "aws-cpp-sdk-kinesis/source/**/*.cpp", - "aws-cpp-sdk-s3/include/**/*.h", - "aws-cpp-sdk-s3/source/**/*.cpp", - "aws-cpp-sdk-transfer/include/**/*.h", - "aws-cpp-sdk-transfer/source/**/*.cpp", + "aws-cpp-sdk-core/source/*.cpp", # AWS_SOURCE + "aws-cpp-sdk-core/source/external/tinyxml2/*.cpp", # AWS_TINYXML2_SOURCE + "aws-cpp-sdk-core/source/external/cjson/*.cpp", # CJSON_SOURCE + "aws-cpp-sdk-core/source/auth/*.cpp", # AWS_AUTH_SOURCE + "aws-cpp-sdk-core/source/client/*.cpp", # AWS_CLIENT_SOURCE + "aws-cpp-sdk-core/source/internal/*.cpp", # AWS_INTERNAL_SOURCE + "aws-cpp-sdk-core/source/aws/model/*.cpp", # AWS_MODEL_SOURCE + "aws-cpp-sdk-core/source/http/*.cpp", # HTTP_SOURCE + "aws-cpp-sdk-core/source/http/standard/*.cpp", # HTTP_STANDARD_SOURCE + "aws-cpp-sdk-core/source/config/*.cpp", # CONFIG_SOURCE + "aws-cpp-sdk-core/source/monitoring/*.cpp", # MONITORING_SOURCE + "aws-cpp-sdk-core/source/utils/*.cpp", # UTILS_SOURCE + "aws-cpp-sdk-core/source/utils/event/*.cpp", # UTILS_EVENT_SOURCE + "aws-cpp-sdk-core/source/utils/base64/*.cpp", # UTILS_BASE64_SOURCE + "aws-cpp-sdk-core/source/utils/crypto/*.cpp", # UTILS_CRYPTO_SOURCE + "aws-cpp-sdk-core/source/utils/json/*.cpp", # UTILS_JSON_SOURCE + "aws-cpp-sdk-core/source/utils/threading/*.cpp", # UTILS_THREADING_SOURCE + "aws-cpp-sdk-core/source/utils/xml/*.cpp", # UTILS_XML_SOURCE + "aws-cpp-sdk-core/source/utils/logging/*.cpp", # UTILS_LOGGING_SOURCE + "aws-cpp-sdk-core/source/utils/memory/*.cpp", # UTILS_MEMORY_SOURCE + "aws-cpp-sdk-core/source/utils/memory/stl/*.cpp", # UTILS_MEMORY_STL_SOURCE + "aws-cpp-sdk-core/source/utils/stream/*.cpp", # UTILS_STREAM_SOURCE + "aws-cpp-sdk-core/source/utils/crypto/factory/*.cpp", # UTILS_CRYPTO_FACTORY_SOURCE + "aws-cpp-sdk-core/source/http/curl/*.cpp", # HTTP_CURL_CLIENT_SOURCE + "aws-cpp-sdk-core/source/utils/crypto/openssl/*.cpp", # UTILS_CRYPTO_OPENSSL_SOURCE ]) + select({ "@bazel_tools//src/conditions:windows": glob([ - "aws-cpp-sdk-core/source/http/windows/*.cpp", - "aws-cpp-sdk-core/source/net/windows/*.cpp", - "aws-cpp-sdk-core/source/platform/windows/*.cpp", + "aws-cpp-sdk-core/source/net/windows/*.cpp", # NET_SOURCE + "aws-cpp-sdk-core/source/platform/windows/*.cpp", # PLATFORM_WINDOWS_SOURCE ]), "//conditions:default": glob([ - "aws-cpp-sdk-core/source/net/linux-shared/*.cpp", - "aws-cpp-sdk-core/source/platform/linux-shared/*.cpp", + "aws-cpp-sdk-core/source/net/linux-shared/*.cpp", # NET_SOURCE + "aws-cpp-sdk-core/source/platform/linux-shared/*.cpp", # PLATFORM_LINUX_SHARED_SOURCE ]), }), hdrs = [ "aws-cpp-sdk-core/include/aws/core/SDKConfig.h", - ], + ] + glob([ + "aws-cpp-sdk-core/include/aws/core/*.h", # AWS_HEADERS + "aws-cpp-sdk-core/include/aws/core/auth/*.h", # AWS_AUTH_HEADERS + "aws-cpp-sdk-core/include/aws/core/client/*.h", # AWS_CLIENT_HEADERS + "aws-cpp-sdk-core/include/aws/core/internal/*.h", # AWS_INTERNAL_HEADERS + "aws-cpp-sdk-core/include/aws/core/net/*.h", # NET_HEADERS + "aws-cpp-sdk-core/include/aws/core/http/*.h", # HTTP_HEADERS + "aws-cpp-sdk-core/include/aws/core/http/standard/*.h", # HTTP_STANDARD_HEADERS + "aws-cpp-sdk-core/include/aws/core/config/*.h", # CONFIG_HEADERS + "aws-cpp-sdk-core/include/aws/core/monitoring/*.h", # MONITORING_HEADERS + "aws-cpp-sdk-core/include/aws/core/platform/*.h", # PLATFORM_HEADERS + "aws-cpp-sdk-core/include/aws/core/utils/*.h", # UTILS_HEADERS + "aws-cpp-sdk-core/include/aws/core/utils/event/*.h", # UTILS_EVENT_HEADERS + "aws-cpp-sdk-core/include/aws/core/utils/base64/*.h", # UTILS_BASE64_HEADERS + "aws-cpp-sdk-core/include/aws/core/utils/crypto/*.h", # UTILS_CRYPTO_HEADERS + "aws-cpp-sdk-core/include/aws/core/utils/json/*.h", # UTILS_JSON_HEADERS + "aws-cpp-sdk-core/include/aws/core/utils/threading/*.h", # UTILS_THREADING_HEADERS + "aws-cpp-sdk-core/include/aws/core/utils/xml/*.h", # UTILS_XML_HEADERS + "aws-cpp-sdk-core/include/aws/core/utils/memory/*.h", # UTILS_MEMORY_HEADERS + "aws-cpp-sdk-core/include/aws/core/utils/memory/stl/*.h", # UTILS_STL_HEADERS + "aws-cpp-sdk-core/include/aws/core/utils/logging/*.h", # UTILS_LOGGING_HEADERS + "aws-cpp-sdk-core/include/aws/core/utils/ratelimiter/*.h", # UTILS_RATE_LIMITER_HEADERS + "aws-cpp-sdk-core/include/aws/core/utils/stream/*.h", # UTILS_STREAM_HEADERS + "aws-cpp-sdk-core/include/aws/core/external/cjson/*.h", # CJSON_HEADERS + "aws-cpp-sdk-core/include/aws/core/external/tinyxml2/*.h", # TINYXML2_HEADERS + "aws-cpp-sdk-core/include/aws/core/http/curl/*.h", # HTTP_CURL_CLIENT_HEADERS + "aws-cpp-sdk-core/include/aws/core/utils/crypto/openssl/*.h", # UTILS_CRYPTO_OPENSSL_HEADERS + ]), defines = [ 'AWS_SDK_VERSION_STRING=\\"1.7.366\\"', "AWS_SDK_VERSION_MAJOR=1", @@ -72,15 +94,7 @@ cc_library( }), includes = [ "aws-cpp-sdk-core/include", - "aws-cpp-sdk-kinesis/include", - "aws-cpp-sdk-s3/include", - "aws-cpp-sdk-transfer/include", - ] + select({ - "@bazel_tools//src/conditions:windows": [ - "aws-cpp-sdk-core/include/aws/core/platform/refs", - ], - "//conditions:default": [], - }), + ], linkopts = select({ "@bazel_tools//src/conditions:windows": [ "-DEFAULTLIB:userenv.lib", @@ -89,14 +103,64 @@ cc_library( "//conditions:default": [], }), deps = [ - "@aws-c-common", "@aws-c-event-stream", - "@aws-checksums", - "@boringssl//:crypto", "@curl", ], ) +cc_library( + name = "s3", + srcs = glob([ + "aws-cpp-sdk-s3/source/*.cpp", # AWS_S3_SOURCE + "aws-cpp-sdk-s3/source/model/*.cpp", # AWS_S3_MODEL_SOURCE + ]), + hdrs = glob([ + "aws-cpp-sdk-s3/include/aws/s3/*.h", # AWS_S3_HEADERS + "aws-cpp-sdk-s3/include/aws/s3/model/*.h", # AWS_S3_MODEL_HEADERS + ]), + includes = [ + "aws-cpp-sdk-s3/include", + ], + deps = [ + ":core", + ], +) + +cc_library( + name = "transfer", + srcs = glob([ + "aws-cpp-sdk-transfer/source/transfer/*.cpp", # TRANSFER_SOURCE + ]), + hdrs = glob([ + "aws-cpp-sdk-transfer/include/aws/transfer/*.h", # TRANSFER_HEADERS + ]), + includes = [ + "aws-cpp-sdk-transfer/include", + ], + deps = [ + ":core", + ":s3", + ], +) + +cc_library( + name = "kinesis", + srcs = glob([ + "aws-cpp-sdk-kinesis/source/*.cpp", # AWS_KINESIS_SOURCE + "aws-cpp-sdk-kinesis/source/model/*.cpp", # AWS_KINESIS_MODEL_SOURCE + ]), + hdrs = glob([ + "aws-cpp-sdk-kinesis/include/aws/kinesis/*.h", # AWS_KINESIS_HEADERS + "aws-cpp-sdk-kinesis/include/aws/kinesis/model/*.h", # AWS_KINESIS_MODEL_HEADERS + ]), + includes = [ + "aws-cpp-sdk-kinesis/include", + ], + deps = [ + ":core", + ], +) + genrule( name = "SDKConfig_h", outs = [ diff --git a/third_party/curl.BUILD b/third_party/curl.BUILD index 6d4d40fa2..0fe66bf9d 100644 --- a/third_party/curl.BUILD +++ b/third_party/curl.BUILD @@ -112,7 +112,7 @@ genrule( "# define HAVE_SYS_FILIO_H 1", "# define HAVE_SYS_SOCKIO_H 1", "# define OS \"x86_64-apple-darwin15.5.0\"", - "# define USE_DARWINSSL 1", + "# define USE_SECTRANSP 1", "#else", "# define CURL_CA_BUNDLE \"/etc/ssl/certs/ca-certificates.crt\"", "# define GETSERVBYPORT_R_ARGS 6", From e29e8a3c7ef91e8075aaf88da5e7f0ac9225f44f Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Wed, 23 Dec 2020 18:22:09 -0800 Subject: [PATCH 20/85] Add missed function RecursivelyCreateDir for hdfs file system implementation (#1218) This PR adds missed function RecursivelyCreateDir for hdfs file system implementation Signed-off-by: Yong Tang --- tensorflow_io/core/plugins/hdfs/hadoop_filesystem.cc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tensorflow_io/core/plugins/hdfs/hadoop_filesystem.cc b/tensorflow_io/core/plugins/hdfs/hadoop_filesystem.cc index 83bdb0a1d..4946a2b3c 100644 --- a/tensorflow_io/core/plugins/hdfs/hadoop_filesystem.cc +++ b/tensorflow_io/core/plugins/hdfs/hadoop_filesystem.cc @@ -774,6 +774,11 @@ void CreateDir(const TF_Filesystem* filesystem, const char* path, TF_SetStatus(status, TF_OK, ""); } +static void RecursivelyCreateDir(const TF_Filesystem* filesystem, + const char* path, TF_Status* status) { + CreateDir(filesystem, path, status); +} + void DeleteDir(const TF_Filesystem* filesystem, const char* path, TF_Status* status) { auto hadoop_file = @@ -931,6 +936,8 @@ void ProvideFilesystemSupportFor(TF_FilesystemPluginOps* ops, const char* uri) { ops->filesystem_ops->new_read_only_memory_region_from_file = tf_hdfs_filesystem::NewReadOnlyMemoryRegionFromFile; ops->filesystem_ops->create_dir = tf_hdfs_filesystem::CreateDir; + ops->filesystem_ops->recursively_create_dir = + tf_hdfs_filesystem::RecursivelyCreateDir; ops->filesystem_ops->delete_file = tf_hdfs_filesystem::DeleteFile; ops->filesystem_ops->delete_dir = tf_hdfs_filesystem::DeleteDir; ops->filesystem_ops->rename_file = tf_hdfs_filesystem::RenameFile; From fd7bc1ade286629174cafb2df72279c7d35161a2 Mon Sep 17 00:00:00 2001 From: Vignesh Kothapalli Date: Sun, 27 Dec 2020 02:39:49 +0530 Subject: [PATCH 21/85] update bazel version to 3.7.2 (#1251) --- .bazelversion | 2 +- tools/docker/devel.Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.bazelversion b/.bazelversion index a0cd9f0cc..47b6be3fa 100644 --- a/.bazelversion +++ b/.bazelversion @@ -1 +1 @@ -3.1.0 \ No newline at end of file +3.7.2 \ No newline at end of file diff --git a/tools/docker/devel.Dockerfile b/tools/docker/devel.Dockerfile index b31f86933..075dc6138 100644 --- a/tools/docker/devel.Dockerfile +++ b/tools/docker/devel.Dockerfile @@ -14,7 +14,7 @@ RUN rm -f /etc/apt/sources.list.d/jonathonf-ubuntu-python-3_6-xenial.list && apt ffmpeg \ dnsutils -ARG BAZEL_VERSION=3.1.0 +ARG BAZEL_VERSION=3.7.2 ARG BAZEL_OS=linux RUN curl -sL https://github.com/bazelbuild/bazel/releases/download/${BAZEL_VERSION}/bazel-${BAZEL_VERSION}-installer-${BAZEL_OS}-x86_64.sh -o bazel-install.sh && \ From 7bb3d303268f2ef2f43186d37954edbd86c57cc2 Mon Sep 17 00:00:00 2001 From: Vignesh Kothapalli Date: Mon, 28 Dec 2020 00:43:28 +0530 Subject: [PATCH 22/85] [audio] cleanup vorbis file after usage (#1249) * [audio] cleanup vorbis file after usage * move the file cleanup to destructor --- tensorflow_io/core/kernels/audio_video_ogg_kernels.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tensorflow_io/core/kernels/audio_video_ogg_kernels.cc b/tensorflow_io/core/kernels/audio_video_ogg_kernels.cc index 7b2633c14..2d2d9350f 100644 --- a/tensorflow_io/core/kernels/audio_video_ogg_kernels.cc +++ b/tensorflow_io/core/kernels/audio_video_ogg_kernels.cc @@ -83,7 +83,10 @@ static ov_callbacks OggVorbisCallbacks = { class OggVorbisReadableResource : public AudioReadableResourceBase { public: OggVorbisReadableResource(Env* env) : env_(env) {} - ~OggVorbisReadableResource() {} + ~OggVorbisReadableResource() { + // Cleanup the vorbis file + ov_clear(&ogg_vorbis_file_); + } Status Init(const string& filename, const void* optional_memory, const size_t optional_length) override { @@ -142,8 +145,8 @@ class OggVorbisReadableResource : public AudioReadableResourceBase { long samples_read = 0; long samples_to_read = value->shape().dim_size(0); + float** buffer; while (samples_read < samples_to_read) { - float** buffer; int bitstream = 0; long chunk = ov_read_float(&ogg_vorbis_file_, &buffer, samples_to_read - samples_read, &bitstream); @@ -160,6 +163,7 @@ class OggVorbisReadableResource : public AudioReadableResourceBase { } samples_read += chunk; } + return Status::OK(); } string DebugString() const override { return "OggVorbisReadableResource"; } From 9966944f41529b0753f07d8d5e1b510ea7290b4c Mon Sep 17 00:00:00 2001 From: Vignesh Kothapalli Date: Mon, 4 Jan 2021 21:23:23 +0530 Subject: [PATCH 23/85] [s3] add support for testing on macOS (#1253) * [s3] add support for testing on macOS * modify docker-compose cmd --- tests/test_aws/aws_test.sh | 11 ++++++----- tests/test_aws/docker-compose.yml | 12 ++++++++++++ tests/test_s3_eager.py | 4 ++-- 3 files changed, 20 insertions(+), 7 deletions(-) create mode 100644 tests/test_aws/docker-compose.yml diff --git a/tests/test_aws/aws_test.sh b/tests/test_aws/aws_test.sh index a4778a48e..1d21494bc 100755 --- a/tests/test_aws/aws_test.sh +++ b/tests/test_aws/aws_test.sh @@ -17,10 +17,11 @@ set -e set -o pipefail -LOCALSTACK_VERSION=0.12.2 -docker pull localstack/localstack:$LOCALSTACK_VERSION -docker run -d --rm --net=host --name=tensorflow-io-aws localstack/localstack:$LOCALSTACK_VERSION -echo "Waiting for 10 secs until localstack is up and running" -sleep 10 +export LOCALSTACK_VERSION=0.12.2 +cd tests/test_aws +TMPDIR=/private$TMPDIR docker-compose up -d +cd - +echo "Waiting for 20 secs until localstack is up and running" +sleep 20 echo "Localstack up" exit 0 diff --git a/tests/test_aws/docker-compose.yml b/tests/test_aws/docker-compose.yml new file mode 100644 index 000000000..3877f5637 --- /dev/null +++ b/tests/test_aws/docker-compose.yml @@ -0,0 +1,12 @@ +version: '2.1' +services: + localstack: + image: "localstack/localstack:${LOCALSTACK_VERSION}" + container_name: tensorflow-io-aws + ports: + - "4566:4566" + environment: + - SERVICES=${SERVICES- } + - DOCKER_HOST=unix:///var/run/docker.sock + volumes: + - "${TMPDIR:-/tmp/localstack}:/tmp/localstack" \ No newline at end of file diff --git a/tests/test_s3_eager.py b/tests/test_s3_eager.py index ad18af1f7..dc8e3ff0c 100644 --- a/tests/test_s3_eager.py +++ b/tests/test_s3_eager.py @@ -24,8 +24,8 @@ @pytest.mark.skipif( - sys.platform in ("win32", "darwin"), - reason="TODO Localstack not setup properly on macOS/Windows yet", + sys.platform in ("win32"), + reason="TODO Localstack not setup properly on Windows yet", ) def test_read_file(): """Test case for reading S3""" From 0342a16d4cc28ec5ffecf9f8dc45bc3b1452f441 Mon Sep 17 00:00:00 2001 From: Cheng Ren Date: Mon, 4 Jan 2021 23:22:04 -0800 Subject: [PATCH 24/85] add notebook formatting instruction in README (#1256) --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 7c339ef65..94abcdea0 100644 --- a/README.md +++ b/README.md @@ -176,6 +176,8 @@ Lint fix an individual python file with black and pyupgrade using: $ bazel run //tools/lint:lint -- black pyupgrade -- tensorflow_io/core/python/ops/version_ops.py ``` +### Notebooks/Tutorials +If you are updating or creating a notebook, please refer to the tutorials and instructions mentioned [here](https://github.com/tensorflow/io/tree/master/docs/tutorials). ### Python From b56ad5d3b84d9e19937487a7ff1f1bd003a75b6d Mon Sep 17 00:00:00 2001 From: Vignesh Kothapalli Date: Tue, 5 Jan 2021 21:56:56 +0530 Subject: [PATCH 25/85] [docs] Restructure README.md content (#1257) * Refactor README.md content * bump to run ci jobs --- .github/workflows/build.yml | 6 +- CONTRIBUTING.md | 6 +- README.md | 346 +++--------------------------------- docs/development.md | 339 +++++++++++++++++++++++++++++++++++ 4 files changed, 364 insertions(+), 333 deletions(-) create mode 100644 docs/development.md diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dc3700ab3..458f704ee 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -55,7 +55,7 @@ jobs: echo $PATH python3 --version python3 -c 'import site; print(site.getsitepackages())' - python3 .github/workflows/build.instruction.py --sudo=true README.md "#### macOS" > source.sh + python3 .github/workflows/build.instruction.py --sudo=true docs/development.md "#### macOS" > source.sh bash -x -e source.sh python3 -c 'import tensorflow as tf; print(tf.version.VERSION)' @@ -67,7 +67,7 @@ jobs: - run: | set -x -e bash -x -e .github/workflows/build.space.sh - python3 .github/workflows/build.instruction.py README.md "##### Ubuntu 20.04" > source.sh + python3 .github/workflows/build.instruction.py docs/development.md "##### Ubuntu 20.04" > source.sh cat source.sh docker run -i --rm -v $PWD:/v -w /v --net=host ubuntu:20.04 \ bash -x -e source.sh @@ -80,7 +80,7 @@ jobs: - run: | set -x -e bash -x -e .github/workflows/build.space.sh - python3 .github/workflows/build.instruction.py README.md "##### CentOS 7" > source.sh + python3 .github/workflows/build.instruction.py docs/development.md "##### CentOS 7" > source.sh cat source.sh docker run -i --rm -v $PWD:/v -w /v --net=host centos:7 \ bash -x -e source.sh diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e8e02ea5f..2c4cc2378 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,7 +1,6 @@ # Contributing -Tensorflow I/O project welcomes all kinds of contributions, be it code changes, bug-fixes or documentation changes. -This guide should help you in taking care of some basic setups & code conventions. +Tensorflow I/O project welcomes all kinds of contributions, be it code changes, bug-fixes or documentation changes. This guide should help you in taking care of some basic setups & code conventions. ## Contributor License Agreement @@ -17,8 +16,7 @@ again. ## Coding Style -Tensorflow project wide code style guidelines can be followed at [TensorFlow Style Guide - Conventions](https://www.tensorflow.org/community/contribute/code_style) and Tensorflow I/O project specific -code style guidelines can be followed at [Style Guide](STYLE_GUIDE.md). +Tensorflow project wide code style guidelines can be followed at [TensorFlow Style Guide - Conventions](https://www.tensorflow.org/community/contribute/code_style) and Tensorflow I/O project specific code style guidelines can be followed at [Style Guide](STYLE_GUIDE.md). ## Code Reviews diff --git a/README.md b/README.md index 94abcdea0..a24ef1b4e 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,8 @@ People who are a little more adventurous can also try our nightly binaries: $ pip install tensorflow-io-nightly ``` +### Docker Images + In addition to the pip packages, the docker images can be used to quickly get started. For stable builds: @@ -132,324 +134,14 @@ of releases [here](https://github.com/tensorflow/io/releases). | 0.2.0 | 1.12.0 | Jan 29, 2019 | | 0.1.0 | 1.12.0 | Dec 16, 2018 | -## Development - -### IDE Setup - -For instructions on how to configure Visual Studio Code for developing TensorFlow I/O, please refer to -https://github.com/tensorflow/io/blob/master/docs/vscode.md - -### Lint - -TensorFlow I/O's code conforms to Bazel Buildifier, Clang Format, Black, and Pyupgrade. -Please use the following command to check the source code and identify lint issues: -``` -$ bazel run //tools/lint:check -``` - -For Bazel Buildifier and Clang Format, the following command will automatically identify -and fix any lint errors: -``` -$ bazel run //tools/lint:lint -``` - -Alternatively, if you only want to perform lint check using individual linters, -then you can selectively pass `black`, `pyupgrade`, `bazel`, or `clang` to the above commands. - -For example, a `black` specific lint check can be done using: -``` -$ bazel run //tools/lint:check -- black -``` - -Lint fix using Bazel Buildifier and Clang Format can be done using: -``` -$ bazel run //tools/lint:lint -- bazel clang -``` - -Lint check using `black` and `pyupgrade` for an individual python file can be done using: -``` -$ bazel run //tools/lint:check -- black pyupgrade -- tensorflow_io/core/python/ops/version_ops.py -``` - -Lint fix an individual python file with black and pyupgrade using: -``` -$ bazel run //tools/lint:lint -- black pyupgrade -- tensorflow_io/core/python/ops/version_ops.py -``` - -### Notebooks/Tutorials -If you are updating or creating a notebook, please refer to the tutorials and instructions mentioned [here](https://github.com/tensorflow/io/tree/master/docs/tutorials). - -### Python - -#### macOS - -On macOS Catalina 10.15.7, it is possible to build tensorflow-io with -system provided python 3.8.2. Both `tensorflow` and `bazel` are needed. - -NOTE: The system default python 3.8.2 on macOS 10.15.7 will cause `regex` installation -error caused by compiler option of `-arch arm64 -arch x86_64` (similar to the issue -mentioned in https://github.com/giampaolo/psutil/issues/1832). To overcome this issue -`export ARCHFLAGS="-arch x86_64"` will be needed to remove arm64 build option. - -```sh -#!/usr/bin/env bash - -# Disable arm64 build by specifying only x86_64 arch. -# Only needed for macOS's system default python 3.8.2 on macOS 10.15.7 -export ARCHFLAGS="-arch x86_64" - -# Use following command to check if Xcode is correctly installed: -xcodebuild -version - -# Show macOS's default python3 -python3 --version - -# Install Bazel version specified in .bazelversion -curl -OL https://github.com/bazelbuild/bazel/releases/download/$(cat .bazelversion)/bazel-$(cat .bazelversion)-installer-darwin-x86_64.sh -sudo bash -x -e bazel-$(cat .bazelversion)-installer-darwin-x86_64.sh - -# Install tensorflow and configure bazel -sudo ./configure.sh - -# Build shared libraries -bazel build -s --verbose_failures //tensorflow_io/... - -# Once build is complete, shared libraries will be available in -# `bazel-bin/tensorflow_io/core/python/ops/` and it is possible -# to run tests with `pytest`, e.g.: -sudo python3 -m pip install pytest -TFIO_DATAPATH=bazel-bin python3 -m pytest -s -v tests/test_serialization_eager.py -``` - -NOTE: When running pytest, `TFIO_DATAPATH=bazel-bin` has to be passed so that python can utilize the generated shared libraries after the build process. - -##### Troubleshoot - -If Xcode is installed, but `$ xcodebuild -version` is not displaying the expected output, you might need to enable Xcode command line with the command: - -`$ xcode-select -s /Applications/Xcode.app/Contents/Developer`. - -A terminal restart might be required for the changes to take effect. - -Sample output: - -``` -$ xcodebuild -version -Xcode 11.6 -Build version 11E708 -``` - - -#### Linux - -Development of tensorflow-io on Linux is similar to macOS. The required packages -are gcc, g++, git, bazel, and python 3. Newer versions of gcc or python, other than the default system installed -versions might be required though. - -##### Ubuntu 20.04 - -Ubuntu 20.04 requires gcc/g++, git, and python 3. The following will install dependencies and build -the shared libraries on Ubuntu 20.04: -```sh -#!/usr/bin/env bash - -# Install gcc/g++, git, unzip/curl (for bazel), and python3 -sudo apt-get -y -qq update -sudo apt-get -y -qq install gcc g++ git unzip curl python3-pip - -# Install Bazel version specified in .bazelversion -curl -sSOL https://github.com/bazelbuild/bazel/releases/download/$(cat .bazelversion)/bazel-$(cat .bazelversion)-installer-linux-x86_64.sh -sudo bash -x -e bazel-$(cat .bazelversion)-installer-linux-x86_64.sh - -# Upgrade pip -sudo python3 -m pip install -U pip - -# Install tensorflow and configure bazel -sudo ./configure.sh - -# Build shared libraries -bazel build -s --verbose_failures //tensorflow_io/... - -# Once build is complete, shared libraries will be available in -# `bazel-bin/tensorflow_io/core/python/ops/` and it is possible -# to run tests with `pytest`, e.g.: -sudo python3 -m pip install pytest -TFIO_DATAPATH=bazel-bin python3 -m pytest -s -v tests/test_serialization_eager.py -``` - -##### CentOS 8 - -The steps to build shared libraries for CentOS 8 is similiar to Ubuntu 20.04 above -excpet that -``` -sudo yum install -y python3 python3-devel gcc gcc-c++ git unzip which make -``` -should be used instead to install gcc/g++, git, unzip/which (for bazel), and python3. - -##### CentOS 7 - -On CentOS 7, the default python and gcc version are too old to build tensorflow-io's shared -libraries (.so). The gcc provided by Developer Toolset and rh-python36 should be used instead. -Also, the libstdc++ has to be linked statically to avoid discrepancy of libstdc++ installed on -CentOS vs. newer gcc version by devtoolset. - -Furthermore, a special flag `--//tensorflow_io/core:static_build` has to be passed to Bazel -in order to avoid duplication of symbols in statically linked libraries for file system -plugins. - -The following will install bazel, devtoolset-9, rh-python36, and build the shared libraries: -```sh -#!/usr/bin/env bash - -# Install centos-release-scl, then install gcc/g++ (devtoolset), git, and python 3 -sudo yum install -y centos-release-scl -sudo yum install -y devtoolset-9 git rh-python36 make - -# Install Bazel version specified in .bazelversion -curl -sSOL https://github.com/bazelbuild/bazel/releases/download/$(cat .bazelversion)/bazel-$(cat .bazelversion)-installer-linux-x86_64.sh -sudo bash -x -e bazel-$(cat .bazelversion)-installer-linux-x86_64.sh - -# Upgrade pip -scl enable rh-python36 devtoolset-9 \ - 'python3 -m pip install -U pip' - -# Install tensorflow and configure bazel with rh-python36 -scl enable rh-python36 devtoolset-9 \ - './configure.sh' - -# Build shared libraries, notice the passing of --//tensorflow_io/core:static_build -BAZEL_LINKOPTS="-static-libstdc++ -static-libgcc" BAZEL_LINKLIBS="-lm -l%:libstdc++.a" \ - scl enable rh-python36 devtoolset-9 \ - 'bazel build -s --verbose_failures --//tensorflow_io/core:static_build //tensorflow_io/...' - -# Once build is complete, shared libraries will be available in -# `bazel-bin/tensorflow_io/core/python/ops/` and it is possible -# to run tests with `pytest`, e.g.: -scl enable rh-python36 devtoolset-9 \ - 'python3 -m pip install pytest' - -TFIO_DATAPATH=bazel-bin \ - scl enable rh-python36 devtoolset-9 \ - 'python3 -m pytest -s -v tests/test_serialization_eager.py' -``` - -#### Python Wheels - -It is possible to build python wheels after bazel build is complete with the following command: -``` -$ python3 setup.py bdist_wheel --data bazel-bin -``` -The .whl file will be available in dist directory. Note the bazel binary directory `bazel-bin` -has to be passed with `--data` args in order for setup.py to locate the necessary share objects, -as `bazel-bin` is outside of the `tensorflow_io` package directory. - -Alternatively, source install could be done with: -``` -$ TFIO_DATAPATH=bazel-bin python3 -m pip install . -``` -with `TFIO_DATAPATH=bazel-bin` passed for the same reason. - -Note installing with `-e` is different from the above. The -``` -$ TFIO_DATAPATH=bazel-bin python3 -m pip install -e . -``` -will not install shared object automatically even with `TFIO_DATAPATH=bazel-bin`. Instead, -`TFIO_DATAPATH=bazel-bin` has to be passed everytime the program is run after the install: -``` -$ TFIO_DATAPATH=bazel-bin python3 - ->>> import tensorflow_io as tfio ->>> ... -``` - -#### Docker - -For Python development, a reference Dockerfile [here](tools/docker/devel.Dockerfile) can be -used to build the TensorFlow I/O package (`tensorflow-io`) from source. Additionally, the -pre-built devel images can be used as well: -```sh -# Pull (if necessary) and start the devel container -$ docker run -it --rm --name tfio-dev --net=host -v ${PWD}:/v -w /v tfsigio/tfio:latest-devel bash - -# Inside the docker container, ./configure.sh will install TensorFlow or use existing install -(tfio-dev) root@docker-desktop:/v$ ./configure.sh - -# Clean up exisiting bazel build's (if any) -(tfio-dev) root@docker-desktop:/v$ rm -rf bazel-* - -# Build TensorFlow I/O C++. For compilation optimization flags, the default (-march=native) -# optimizes the generated code for your machine's CPU type. -# Reference: https://www.tensorflow.orginstall/source#configuration_options). - -# NOTE: Based on the available resources, please change the number of job workers to: -# -j 4/8/16 to prevent bazel server terminations and resource oriented build errors. - -(tfio-dev) root@docker-desktop:/v$ bazel build -j 8 --copt=-msse4.2 --copt=-mavx --compilation_mode=opt --verbose_failures --test_output=errors --crosstool_top=//third_party/toolchains/gcc7_manylinux2010:toolchain //tensorflow_io/... - - -# Run tests with PyTest, note: some tests require launching additional containers to run (see below) -(tfio-dev) root@docker-desktop:/v$ pytest -s -v tests/ -# Build the TensorFlow I/O package -(tfio-dev) root@docker-desktop:/v$ python setup.py bdist_wheel -``` - -A package file `dist/tensorflow_io-*.whl` will be generated after a build is successful. - -NOTE: When working in the Python development container, an environment variable -`TFIO_DATAPATH` is automatically set to point tensorflow-io to the shared C++ -libraries built by Bazel to run `pytest` and build the `bdist_wheel`. Python -`setup.py` can also accept `--data [path]` as an argument, for example -`python setup.py --data bazel-bin bdist_wheel`. - -NOTE: While the tfio-dev container gives developers an easy to work with -environment, the released whl packages are built differently due to manylinux2010 -requirements. Please check [Build Status and CI] section for more details -on how the released whl packages are generated. - -#### Starting Test Containers - -Some tests require launching a test container before running. In order -to run all tests, execute the following commands: - -```sh -$ bash -x -e tests/test_ignite/start_ignite.sh -$ bash -x -e tests/test_kafka/kafka_test.sh -$ bash -x -e tests/test_kinesis/kinesis_test.sh -``` - -### R - -We provide a reference Dockerfile [here](R-package/scripts/Dockerfile) for you -so that you can use the R package directly for testing. You can build it via: -```sh -$ docker build -t tfio-r-dev -f R-package/scripts/Dockerfile . -``` - -Inside the container, you can start your R session, instantiate a `SequenceFileDataset` -from an example [Hadoop SequenceFile](https://wiki.apache.org/hadoop/SequenceFile) -[string.seq](R-package/tests/testthat/testdata/string.seq), and then use any [transformation functions](https://tensorflow.rstudio.com/tools/tfdatasets/articles/introduction.html#transformations) provided by [tfdatasets package](https://tensorflow.rstudio.com/tools/tfdatasets/) on the dataset like the following: - -```r -library(tfio) -dataset <- sequence_file_dataset("R-package/tests/testthat/testdata/string.seq") %>% - dataset_repeat(2) - -sess <- tf$Session() -iterator <- make_iterator_one_shot(dataset) -next_batch <- iterator_get_next(iterator) - -until_out_of_range({ - batch <- sess$run(next_batch) - print(batch) -}) -``` - ## Contributing Tensorflow I/O is a community led open source project. As such, the project -depends on public contributions, bug-fixes, and documentation. Please -see [contribution guidelines](CONTRIBUTING.md) for a guide on how to -contribute. +depends on public contributions, bug-fixes, and documentation. Please see: + +- [contribution guidelines](CONTRIBUTING.md) for a guide on how to contribute. +- [development doc](docs/development.md) for instructions on the development environment setup. +- [tutorials](docs/tutorials) for a list of tutorial notebooks and instructions on how to write one. ### Build Status and CI @@ -481,7 +173,7 @@ It takes some time to build, but once complete, there will be python `3.5`, `3.6`, `3.7` compatible whl packages available in `wheelhouse` directory. -On macOS, the same command could be used though the script expect `python` in shell +On macOS, the same command could be used. However, the script expects `python` in shell and will only generate a whl package that matches the version of `python` in shell. If you want to build a whl package for a specific python then you have to alias this version of python to `python` in shell. See [.github/workflows/build.yml](.github/workflows/build.yml) @@ -493,17 +185,16 @@ TensorFlow I/O uses both GitHub Workflows and Google CI (Kokoro) for continuous GitHub Workflows is used for macOS build and test. Kokoro is used for Linux build and test. Again, because of the manylinux2010 requirement, on Linux whl packages are always built with Ubuntu 16.04 + Developer Toolset 7. Tests are done on a variatiy of systems -with different python version to ensure a good coverage: +with different python3 versions to ensure a good coverage: -| Python | Ubuntu 16.04| Ubuntu 18.04 | macOS + osx9 | -| ------- | ----- | ------- | ------- | -| 2.7 | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | -| 3.5 | :heavy_check_mark: | N/A | :heavy_check_mark: | -| 3.6 | N/A | :heavy_check_mark: | :heavy_check_mark: | -| 3.7 | N/A | :heavy_check_mark: | N/A | +| Python | Ubuntu 18.04| Ubuntu 20.04 | macOS + osx9 | Windows-2019 | +| ------- | ----- | ------- | ------- | --------- | +| 2.7 | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | N/A | +| 3.7 | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | +| 3.8 | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | -TensorFlow I/O has integrations with may systems and cloud vendors such as +TensorFlow I/O has integrations with many systems and cloud vendors such as Prometheus, Apache Kafka, Apache Ignite, Google Cloud PubSub, AWS Kinesis, Microsoft Azure Storage, Alibaba Cloud OSS etc. @@ -526,8 +217,11 @@ level of coverage as live systems or emulators. | AWS Kinesis | | :heavy_check_mark: |:heavy_check_mark:| | | Alibaba Cloud OSS | | | | :heavy_check_mark: | | Google BigTable/BigQuery | | to be added | | | +| Elasticsearch (experimental) | :heavy_check_mark: | |:heavy_check_mark:| | +| MongoDB (experimental) | :heavy_check_mark: | |:heavy_check_mark:| | + -Note: +References for emulators: - Official [PubSub Emulator](https://cloud.google.com/sdk/gcloud/reference/beta/emulators/pubsub/) by Google Cloud for Cloud PubSub. - Official [Azurite Emulator](https://github.com/Azure/Azurite) by Azure for Azure Storage. - None-official [LocalStack emulator](https://github.com/localstack/localstack) by LocalStack for AWS Kinesis. @@ -539,7 +233,7 @@ Note: * SIG IO [Monthly Meeting Notes](https://docs.google.com/document/d/1CB51yJxns5WA4Ylv89D-a5qReiGTC0GYum6DU-9nKGo/edit) * Gitter room: [tensorflow/sig-io](https://gitter.im/tensorflow/sig-io) -## More Information +## Additional Information * [Streaming Machine Learning with Tiered Storage and Without a Data Lake](https://www.confluent.io/blog/streaming-machine-learning-with-tiered-storage/) - [Kai Waehner](https://github.com/kaiwaehner) * [TensorFlow with Apache Arrow Datasets](https://medium.com/tensorflow/tensorflow-with-apache-arrow-datasets-cdbcfe80a59f) - [Bryan Cutler](https://github.com/BryanCutler) diff --git a/docs/development.md b/docs/development.md new file mode 100644 index 000000000..2e690f46a --- /dev/null +++ b/docs/development.md @@ -0,0 +1,339 @@ + +## Development + +The document contains the necessary information for setting up the development environement +and building the `tensorflow-io` package from source on various platforms. + +### IDE Setup + +For instructions on how to configure Visual Studio Code for developing TensorFlow I/O, please refer to this [doc](https://github.com/tensorflow/io/blob/master/docs/vscode.md). + +### Lint + +TensorFlow I/O's code conforms to Bazel Buildifier, Clang Format, Black, and Pyupgrade. +Please use the following command to check the source code and identify lint issues: +``` +$ bazel run //tools/lint:check +``` + +For Bazel Buildifier and Clang Format, the following command will automatically identify +and fix any lint errors: +``` +$ bazel run //tools/lint:lint +``` + +Alternatively, if you only want to perform lint check using individual linters, +then you can selectively pass `black`, `pyupgrade`, `bazel`, or `clang` to the above commands. + +For example, a `black` specific lint check can be done using: +``` +$ bazel run //tools/lint:check -- black +``` + +Lint fix using Bazel Buildifier and Clang Format can be done using: +``` +$ bazel run //tools/lint:lint -- bazel clang +``` + +Lint check using `black` and `pyupgrade` for an individual python file can be done using: +``` +$ bazel run //tools/lint:check -- black pyupgrade -- tensorflow_io/core/python/ops/version_ops.py +``` + +Lint fix an individual python file with black and pyupgrade using: +``` +$ bazel run //tools/lint:lint -- black pyupgrade -- tensorflow_io/core/python/ops/version_ops.py +``` + +### Python + +#### macOS + +On macOS Catalina 10.15.7, it is possible to build tensorflow-io with +system provided python 3.8.2. Both `tensorflow` and `bazel` are needed to do so. + +NOTE: The system default python 3.8.2 on macOS 10.15.7 will cause `regex` installation +error caused by compiler option of `-arch arm64 -arch x86_64` (similar to the issue +mentioned in https://github.com/giampaolo/psutil/issues/1832). To overcome this issue +`export ARCHFLAGS="-arch x86_64"` will be needed to remove arm64 build option. + +```sh +#!/usr/bin/env bash + +# Disable arm64 build by specifying only x86_64 arch. +# Only needed for macOS's system default python 3.8.2 on macOS 10.15.7 +export ARCHFLAGS="-arch x86_64" + +# Use following command to check if Xcode is correctly installed: +xcodebuild -version + +# Show macOS's default python3 +python3 --version + +# Install Bazel version specified in .bazelversion +curl -OL https://github.com/bazelbuild/bazel/releases/download/$(cat .bazelversion)/bazel-$(cat .bazelversion)-installer-darwin-x86_64.sh +sudo bash -x -e bazel-$(cat .bazelversion)-installer-darwin-x86_64.sh + +# Install tensorflow and configure bazel +sudo ./configure.sh + +# Build shared libraries +bazel build -s --verbose_failures //tensorflow_io/... + +# Once build is complete, shared libraries will be available in +# `bazel-bin/tensorflow_io/core/python/ops/` and it is possible +# to run tests with `pytest`, e.g.: +sudo python3 -m pip install pytest +TFIO_DATAPATH=bazel-bin python3 -m pytest -s -v tests/test_serialization_eager.py +``` + +NOTE: When running pytest, `TFIO_DATAPATH=bazel-bin` has to be passed so that python can utilize the generated shared libraries after the build process. + +##### Troubleshoot + +If Xcode is installed, but `$ xcodebuild -version` is not displaying the expected output, you might need to enable Xcode command line with the command: + +`$ xcode-select -s /Applications/Xcode.app/Contents/Developer`. + +A terminal restart might be required for the changes to take effect. + +Sample output: + +``` +$ xcodebuild -version +Xcode 12.2 +Build version 12B45b +``` + +#### Linux + +Development of tensorflow-io on Linux is similar to macOS. The required packages +are gcc, g++, git, bazel, and python 3. Newer versions of gcc or python, other than the default system installed +versions might be required though. + +##### Ubuntu 20.04 + +Ubuntu 20.04 requires gcc/g++, git, and python 3. The following will install dependencies and build +the shared libraries on Ubuntu 20.04: +```sh +#!/usr/bin/env bash + +# Install gcc/g++, git, unzip/curl (for bazel), and python3 +sudo apt-get -y -qq update +sudo apt-get -y -qq install gcc g++ git unzip curl python3-pip + +# Install Bazel version specified in .bazelversion +curl -sSOL https://github.com/bazelbuild/bazel/releases/download/$(cat .bazelversion)/bazel-$(cat .bazelversion)-installer-linux-x86_64.sh +sudo bash -x -e bazel-$(cat .bazelversion)-installer-linux-x86_64.sh + +# Upgrade pip +sudo python3 -m pip install -U pip + +# Install tensorflow and configure bazel +sudo ./configure.sh + +# Build shared libraries +bazel build -s --verbose_failures //tensorflow_io/... + +# Once build is complete, shared libraries will be available in +# `bazel-bin/tensorflow_io/core/python/ops/` and it is possible +# to run tests with `pytest`, e.g.: +sudo python3 -m pip install pytest +TFIO_DATAPATH=bazel-bin python3 -m pytest -s -v tests/test_serialization_eager.py +``` + +##### CentOS 8 + +The steps to build shared libraries for CentOS 8 is similiar to Ubuntu 20.04 above +excpet that +``` +sudo yum install -y python3 python3-devel gcc gcc-c++ git unzip which make +``` +should be used instead to install gcc/g++, git, unzip/which (for bazel), and python3. + +##### CentOS 7 + +On CentOS 7, the default python and gcc version are too old to build tensorflow-io's shared +libraries (.so). The gcc provided by Developer Toolset and rh-python36 should be used instead. +Also, the libstdc++ has to be linked statically to avoid discrepancy of libstdc++ installed on +CentOS vs. newer gcc version by devtoolset. + +Furthermore, a special flag `--//tensorflow_io/core:static_build` has to be passed to Bazel +in order to avoid duplication of symbols in statically linked libraries for file system +plugins. + +The following will install bazel, devtoolset-9, rh-python36, and build the shared libraries: +```sh +#!/usr/bin/env bash + +# Install centos-release-scl, then install gcc/g++ (devtoolset), git, and python 3 +sudo yum install -y centos-release-scl +sudo yum install -y devtoolset-9 git rh-python36 make + +# Install Bazel version specified in .bazelversion +curl -sSOL https://github.com/bazelbuild/bazel/releases/download/$(cat .bazelversion)/bazel-$(cat .bazelversion)-installer-linux-x86_64.sh +sudo bash -x -e bazel-$(cat .bazelversion)-installer-linux-x86_64.sh + +# Upgrade pip +scl enable rh-python36 devtoolset-9 \ + 'python3 -m pip install -U pip' + +# Install tensorflow and configure bazel with rh-python36 +scl enable rh-python36 devtoolset-9 \ + './configure.sh' + +# Build shared libraries, notice the passing of --//tensorflow_io/core:static_build +BAZEL_LINKOPTS="-static-libstdc++ -static-libgcc" BAZEL_LINKLIBS="-lm -l%:libstdc++.a" \ + scl enable rh-python36 devtoolset-9 \ + 'bazel build -s --verbose_failures --//tensorflow_io/core:static_build //tensorflow_io/...' + +# Once build is complete, shared libraries will be available in +# `bazel-bin/tensorflow_io/core/python/ops/` and it is possible +# to run tests with `pytest`, e.g.: +scl enable rh-python36 devtoolset-9 \ + 'python3 -m pip install pytest' + +TFIO_DATAPATH=bazel-bin \ + scl enable rh-python36 devtoolset-9 \ + 'python3 -m pytest -s -v tests/test_serialization_eager.py' +``` + +#### Python Wheels + +It is possible to build python wheels after bazel build is complete with the following command: +``` +$ python3 setup.py bdist_wheel --data bazel-bin +``` +The .whl file will be available in dist directory. Note the bazel binary directory `bazel-bin` +has to be passed with `--data` args in order for setup.py to locate the necessary share objects, +as `bazel-bin` is outside of the `tensorflow_io` package directory. + +Alternatively, source install could be done with: +``` +$ TFIO_DATAPATH=bazel-bin python3 -m pip install . +``` +with `TFIO_DATAPATH=bazel-bin` passed for the same reason. + +Note installing with `-e` is different from the above. The +``` +$ TFIO_DATAPATH=bazel-bin python3 -m pip install -e . +``` +will not install shared object automatically even with `TFIO_DATAPATH=bazel-bin`. Instead, +`TFIO_DATAPATH=bazel-bin` has to be passed everytime the program is run after the install: +``` +$ TFIO_DATAPATH=bazel-bin python3 + +>>> import tensorflow_io as tfio +>>> ... +``` + +#### Docker + +For Python development, a reference Dockerfile [here](tools/docker/devel.Dockerfile) can be +used to build the TensorFlow I/O package (`tensorflow-io`) from source. Additionally, the +pre-built devel images can be used as well: +```sh +# Pull (if necessary) and start the devel container +$ docker run -it --rm --name tfio-dev --net=host -v ${PWD}:/v -w /v tfsigio/tfio:latest-devel bash + +# Inside the docker container, ./configure.sh will install TensorFlow or use existing install +(tfio-dev) root@docker-desktop:/v$ ./configure.sh + +# Clean up exisiting bazel build's (if any) +(tfio-dev) root@docker-desktop:/v$ rm -rf bazel-* + +# Build TensorFlow I/O C++. For compilation optimization flags, the default (-march=native) +# optimizes the generated code for your machine's CPU type. +# Reference: https://www.tensorflow.orginstall/source#configuration_options). + +# NOTE: Based on the available resources, please change the number of job workers to: +# -j 4/8/16 to prevent bazel server terminations and resource oriented build errors. + +(tfio-dev) root@docker-desktop:/v$ bazel build -j 8 --copt=-msse4.2 --copt=-mavx --compilation_mode=opt --verbose_failures --test_output=errors --crosstool_top=//third_party/toolchains/gcc7_manylinux2010:toolchain //tensorflow_io/... + + +# Run tests with PyTest, note: some tests require launching additional containers to run (see below) +(tfio-dev) root@docker-desktop:/v$ pytest -s -v tests/ +# Build the TensorFlow I/O package +(tfio-dev) root@docker-desktop:/v$ python setup.py bdist_wheel +``` + +A package file `dist/tensorflow_io-*.whl` will be generated after a build is successful. + +NOTE: When working in the Python development container, an environment variable +`TFIO_DATAPATH` is automatically set to point tensorflow-io to the shared C++ +libraries built by Bazel to run `pytest` and build the `bdist_wheel`. Python +`setup.py` can also accept `--data [path]` as an argument, for example +`python setup.py --data bazel-bin bdist_wheel`. + +NOTE: While the tfio-dev container gives developers an easy to work with +environment, the released whl packages are built differently due to manylinux2010 +requirements. Please check [Build Status and CI] section for more details +on how the released whl packages are generated. + +#### Testing + +Some tests require launching a test container or start a local instance +of the associated tool before running. For example, to run kafka +related tests which will start a local instance of kafka, zookeeper and schema-registry, +use: + +```sh +# Start the local instances of kafka, zookeeper and schema-registry +$ bash -x -e tests/test_kafka/kafka_test.sh + +# Run the tests +$ TFIO_DATAPATH=bazel-bin pytest -s -vv tests/test_kafka_eager.py +``` + +Testing `Datasets` associated with tools such as `Elasticsearch` or `MongoDB` +require docker to be available on the system. In such scenarios, use: + + +```sh +# Start elasticsearch within docker container +$ bash tests/test_elasticsearch/elasticsearch_test.sh start + +# Run the tests +$ TFIO_DATAPATH=bazel-bin pytest -s -vv tests/test_elasticsearch_eager.py + +# Stop and remove the container +$ bash tests/test_elasticsearch/elasticsearch_test.sh stop +``` + +Additionally, testing some features of `tensorflow-io` doesn't require you to spin up +any additional tools as the data has been provided in the `tests` directory itself. +For example, to run tests related to `parquet` dataset's, use: + +```sh +# Just run the test +$ TFIO_DATAPATH=bazel-bin pytest -s -vv tests/test_parquet_eager.py +``` + + +### R + +We provide a reference Dockerfile [here](R-package/scripts/Dockerfile) for you +so that you can use the R package directly for testing. You can build it via: +```sh +$ docker build -t tfio-r-dev -f R-package/scripts/Dockerfile . +``` + +Inside the container, you can start your R session, instantiate a `SequenceFileDataset` +from an example [Hadoop SequenceFile](https://wiki.apache.org/hadoop/SequenceFile) +[string.seq](R-package/tests/testthat/testdata/string.seq), and then use any [transformation functions](https://tensorflow.rstudio.com/tools/tfdatasets/articles/introduction.html#transformations) provided by [tfdatasets package](https://tensorflow.rstudio.com/tools/tfdatasets/) on the dataset like the following: + +```r +library(tfio) +dataset <- sequence_file_dataset("R-package/tests/testthat/testdata/string.seq") %>% + dataset_repeat(2) + +sess <- tf$Session() +iterator <- make_iterator_one_shot(dataset) +next_batch <- iterator_get_next(iterator) + +until_out_of_range({ + batch <- sess$run(next_batch) + print(batch) +}) +``` \ No newline at end of file From ab9004d76c9f96d505ac54452df4a0a2eeec7876 Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Tue, 5 Jan 2021 11:44:41 -0800 Subject: [PATCH 26/85] Update libtiff/libgeotiff dependency (#1258) This PR updates libtiff/libgeotiff to the latest version. Signed-off-by: Yong Tang --- WORKSPACE | 24 ++++++++++++------------ third_party/proj.BUILD | 3 +++ 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/WORKSPACE b/WORKSPACE index dd980c7ef..d7af70def 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -142,22 +142,22 @@ new_git_repository( http_archive( name = "libgeotiff", build_file = "//third_party:libgeotiff.BUILD", - sha256 = "12c26422e89da7032efcd60d48f3d82c7c0b4c9f3f61aa30c5e3df512946c6cf", - strip_prefix = "libgeotiff-1.5.1", + sha256 = "9452dadd126223a22ce6b97d202066d3873792aaefa7ce739519635a3fe34034", + strip_prefix = "libgeotiff-1.6.0", urls = [ - "https://storage.googleapis.com/mirror.tensorflow.org/download.osgeo.org/geotiff/libgeotiff/libgeotiff-1.5.1.zip", - "https://download.osgeo.org/geotiff/libgeotiff/libgeotiff-1.5.1.zip", + "https://storage.googleapis.com/mirror.tensorflow.org/download.osgeo.org/geotiff/libgeotiff/libgeotiff-1.6.0.zip", + "https://download.osgeo.org/geotiff/libgeotiff/libgeotiff-1.6.0.zip", ], ) http_archive( name = "proj", build_file = "//third_party:proj.BUILD", - sha256 = "0b157e1aa81df4d0dbd89368a0005916bb717f0c09143b4dbc1b20d59204e9f2", - strip_prefix = "proj-6.2.0", + sha256 = "219c6e11b2baa9a3e2bd7ec54ce19702909591032cf6f7d1004b406f10b7c9ad", + strip_prefix = "proj-7.2.1", urls = [ - "https://storage.googleapis.com/mirror.tensorflow.org/download.osgeo.org/proj/proj-6.2.0.zip", - "https://download.osgeo.org/proj/proj-6.2.0.zip", + "https://storage.googleapis.com/mirror.tensorflow.org/download.osgeo.org/proj/proj-7.2.1.zip", + "https://download.osgeo.org/proj/proj-7.2.1.zip", ], ) @@ -316,11 +316,11 @@ http_archive( http_archive( name = "libtiff", build_file = "//third_party:libtiff.BUILD", - sha256 = "5d29f32517dadb6dbcd1255ea5bbc93a2b54b94fbf83653b4d65c7d6775b8634", - strip_prefix = "tiff-4.1.0", + sha256 = "eb0484e568ead8fa23b513e9b0041df7e327f4ee2d22db5a533929dfc19633cb", + strip_prefix = "tiff-4.2.0", urls = [ - "https://storage.googleapis.com/mirror.tensorflow.org/download.osgeo.org/libtiff/tiff-4.1.0.tar.gz", - "https://download.osgeo.org/libtiff/tiff-4.1.0.tar.gz", + "https://storage.googleapis.com/mirror.tensorflow.org/download.osgeo.org/libtiff/tiff-4.2.0.tar.gz", + "https://download.osgeo.org/libtiff/tiff-4.2.0.tar.gz", ], ) diff --git a/third_party/proj.BUILD b/third_party/proj.BUILD index 4235e3e01..854d730e3 100644 --- a/third_party/proj.BUILD +++ b/third_party/proj.BUILD @@ -12,8 +12,11 @@ cc_library( "src/*.c", "src/*.cpp", "src/iso19111/*.cpp", + "src/iso19111/operation/*.cpp", + "src/iso19111/operation/*.hpp", "src/projections/*.cpp", "src/transformations/*.cpp", + "src/transformations/*.hpp", "src/conversions/*.cpp", ], exclude = [ From 0381b918913c14547b1c24475f7cfaa124c7e1fd Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Tue, 5 Jan 2021 15:26:04 -0800 Subject: [PATCH 27/85] Update openjpeg to 2.4.0 (#1259) This PR updates openjpeg to the latest 2.4.0 Signed-off-by: Yong Tang --- WORKSPACE | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/WORKSPACE b/WORKSPACE index d7af70def..1a46473be 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -305,11 +305,11 @@ http_archive( http_archive( name = "openjpeg", build_file = "//third_party:openjpeg.BUILD", - sha256 = "63f5a4713ecafc86de51bfad89cc07bb788e9bba24ebbf0c4ca637621aadb6a9", - strip_prefix = "openjpeg-2.3.1", + sha256 = "8702ba68b442657f11aaeb2b338443ca8d5fb95b0d845757968a7be31ef7f16d", + strip_prefix = "openjpeg-2.4.0", urls = [ - "https://storage.googleapis.com/mirror.tensorflow.org/github.com/uclouvain/openjpeg/archive/v2.3.1.tar.gz", - "https://github.com/uclouvain/openjpeg/archive/v2.3.1.tar.gz", + "https://storage.googleapis.com/mirror.tensorflow.org/github.com/uclouvain/openjpeg/archive/v2.4.0.tar.gz", + "https://github.com/uclouvain/openjpeg/archive/v2.4.0.tar.gz", ], ) From ac8da585b47ad4e46c7188480768fca7ccae7933 Mon Sep 17 00:00:00 2001 From: Vignesh Kothapalli Date: Wed, 6 Jan 2021 21:27:25 +0530 Subject: [PATCH 28/85] [arrow] using eager exec for examples in README.md (#1261) --- tensorflow_io/arrow/README.md | 31 ++++++++----------------------- 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/tensorflow_io/arrow/README.md b/tensorflow_io/arrow/README.md index e0454cbc9..72dccf3e0 100644 --- a/tensorflow_io/arrow/README.md +++ b/tensorflow_io/arrow/README.md @@ -20,12 +20,11 @@ import tensorflow_io.arrow as arrow_io # Assume `df` is an existing Pandas DataFrame dataset = arrow_io.ArrowDataset.from_pandas(df) -iterator = dataset.make_one_shot_iterator() -next_element = iterator.get_next() +# All `tf.data.Dataset` operations can now be performed, for ex: +dataset = dataset.batch(2) -with tf.Session() as sess: - for i in range(len(df)): - print(sess.run(next_element)) +for row in dataset: + print(row) ``` NOTE: The entire DataFrame will be serialized to the Dataset and is not @@ -59,16 +58,9 @@ dataset = arrow_io.ArrowFeatherDataset( output_types=(tf.int32, tf.float32), output_shapes=([], [])) -iterator = dataset.make_one_shot_iterator() -next_element = iterator.get_next() - # This will iterate over each row of each file provided -with tf.Session() as sess: - while True: - try: - print(sess.run(next_element)) - except tf.errors.OutOfRangeError: - break +for row in dataset: + print(row) ``` An alternate constructor can also be used to infer output types and shapes from @@ -101,17 +93,10 @@ dataset = arrow_io.ArrowStreamDataset( output_types=(tf.int32, tf.float32), output_shapes=([], [])) -iterator = dataset.make_one_shot_iterator() -next_element = iterator.get_next() - # The host connection is made when the Dataset op is run and will iterate over # each row of each record batch until the Arrow stream is finished -with tf.Session() as sess: - while True: - try: - print(sess.run(next_element)) - except tf.errors.OutOfRangeError: - break +for row in dataset: + print(row) ``` An alternate constructor can also be used to infer output types and shapes from From 3641d2a1a9d3fb2fc9bde2f54f229c1c94f0fc18 Mon Sep 17 00:00:00 2001 From: Vignesh Kothapalli Date: Thu, 7 Jan 2021 02:42:20 +0530 Subject: [PATCH 29/85] remove unstable elasticsearch test setup on macOS (#1263) --- .github/workflows/build.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 458f704ee..6093357a2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -183,7 +183,6 @@ jobs: bash -x -e tests/test_pubsub/pubsub_test.sh bash -x -e tests/test_aws/aws_test.sh bash -x -e tests/test_pulsar/pulsar_test.sh - bash -x -e tests/test_elasticsearch/elasticsearch_test.sh start bash -x -e tests/test_mongodb/mongodb_test.sh start - name: Install ${{ matrix.python }} macOS run: | From 4f340e0e87aee207b2d0697ebe126fcbf0be6403 Mon Sep 17 00:00:00 2001 From: Irene Onyeneho Date: Wed, 6 Jan 2021 21:13:08 -0800 Subject: [PATCH 30/85] Exposes num_parallel_reads and num_parallel_calls (#1232) * Exposes num_parallel_reads and num_parallel_calls -Exposes `num_parallel_reads` and `num_parallel_calls` in AvroRecordDataset and `make_avro_record_dataset` -Adds parameter constraints -Fixes lint issues * Exposes num_parallel_reads and num_parallel_calls -Exposes `num_parallel_reads` and `num_parallel_calls` in AvroRecordDataset and `make_avro_record_dataset` -Adds parameter constraints -Fixes lint issues * Exposes num_parallel_reads and num_parallel_calls -Exposes `num_parallel_reads` and `num_parallel_calls` in AvroRecordDataset and `make_avro_record_dataset` -Adds parameter constraints -Fixes lint issues * Fixes Lint Issues * Removes Optional typing for method parameter - * Adds test method for _require() function -This update adds a test to check if ValueErrors are raised when given an invalid input for num_parallel_calls * Uncomments skip for macOS pytests * Fixes Lint issues Co-authored-by: Abin Shahab --- .../experimental/avro_record_dataset_ops.py | 147 +++++++++++++++--- .../experimental/make_avro_record_dataset.py | 39 +---- tests/test_parse_avro_eager.py | 57 +++++++ 3 files changed, 189 insertions(+), 54 deletions(-) diff --git a/tensorflow_io/core/python/experimental/avro_record_dataset_ops.py b/tensorflow_io/core/python/experimental/avro_record_dataset_ops.py index b84a97cf4..6429aac10 100644 --- a/tensorflow_io/core/python/experimental/avro_record_dataset_ops.py +++ b/tensorflow_io/core/python/experimental/avro_record_dataset_ops.py @@ -21,6 +21,24 @@ _DEFAULT_READER_SCHEMA = "" # From https://github.com/tensorflow/tensorflow/blob/v2.0.0/tensorflow/python/data/ops/readers.py + +def _require(condition: bool, err_msg: str = None) -> None: + """Checks if the specified condition is true else raises exception + + Args: + condition: The condition to test + err_msg: If specified, it's the error message to use if condition is not true. + + Raises: + ValueError: Raised when the condition is false + + Returns: + None + """ + if not condition: + raise ValueError(err_msg) + + # copied from https://github.com/tensorflow/tensorflow/blob/ # 3095681b8649d9a828afb0a14538ace7a998504d/tensorflow/python/data/ops/readers.py#L36 def _create_or_validate_filenames_dataset(filenames): @@ -52,21 +70,62 @@ def _create_or_validate_filenames_dataset(filenames): # copied from https://github.com/tensorflow/tensorflow/blob/ # 3095681b8649d9a828afb0a14538ace7a998504d/tensorflow/python/data/ops/readers.py#L67 -def _create_dataset_reader(dataset_creator, filenames, num_parallel_reads=None): - """create_dataset_reader""" - - def read_one_file(filename): - filename = tf.convert_to_tensor(filename, tf.string, name="filename") - return dataset_creator(filename) - - if num_parallel_reads is None: - return filenames.flat_map(read_one_file) - if num_parallel_reads == tf.data.experimental.AUTOTUNE: - return filenames.interleave( - read_one_file, num_parallel_calls=num_parallel_reads - ) +def _create_dataset_reader( + dataset_creator, + filenames, + cycle_length=None, + num_parallel_calls=None, + deterministic=None, + block_length=1, +): + """ + This creates a dataset reader which reads records from multiple files and interleaves them together +``` +dataset = Dataset.range(1, 6) # ==> [ 1, 2, 3, 4, 5 ] +# NOTE: New lines indicate "block" boundaries. +dataset = dataset.interleave( + lambda x: Dataset.from_tensors(x).repeat(6), + cycle_length=2, block_length=4) +list(dataset.as_numpy_iterator()) +``` +Results in the following output: +[1,1,1,1, + 2,2,2,2, + 1,1, + 2,2, + 3,3,3,3, + 4,4,4,4, + 3,4, + 5,5,5,5, + 5,5, +] + Args: + dataset_creator: Initializer for AvroDatasetRecord + filenames: A `tf.data.Dataset` iterator of filenames to read + cycle_length: The number of files to be processed in parallel. This is used by `Dataset.Interleave`. + We set this equal to `block_length`, so that each time n number of records are returned for each of the n + files. + num_parallel_calls: Number of threads spawned by the interleave call. + deterministic: Sets whether the interleaved records are written in deterministic order. in tf.interleave this is default true + block_length: Sets the number of output on the output tensor. Defaults to 1 + Returns: + A dataset iterator with an interleaved list of parsed avro records. + + """ + + def read_many_files(filenames): + filenames = tf.convert_to_tensor(filenames, tf.string, name="filename") + return dataset_creator(filenames) + + if cycle_length is None: + return filenames.flat_map(read_many_files) + return filenames.interleave( - read_one_file, cycle_length=num_parallel_reads, block_length=1 + read_many_files, + cycle_length=cycle_length, + num_parallel_calls=num_parallel_calls, + block_length=block_length, + deterministic=deterministic, ) @@ -128,10 +187,16 @@ class AvroRecordDataset(tf.data.Dataset): """A `Dataset` comprising records from one or more AvroRecord files.""" def __init__( - self, filenames, buffer_size=None, num_parallel_reads=None, reader_schema=None + self, + filenames, + buffer_size=None, + num_parallel_reads=None, + num_parallel_calls=None, + reader_schema=None, + deterministic=True, + block_length=1, ): """Creates a `AvroRecordDataset` to read one or more AvroRecord files. - Args: filenames: A `tf.string` tensor or `tf.data.Dataset` containing one or more filenames. @@ -144,25 +209,61 @@ def __init__( files read in parallel are outputted in an interleaved order. If your input pipeline is I/O bottlenecked, consider setting this parameter to a value greater than one to parallelize the I/O. If `None`, files will be - read sequentially. + read sequentially. This must be set to equal or greater than `num_parallel_calls`. + This constraint exists because `num_parallel_reads` becomes `cycle_length` in the + underlying call to `tf.Dataset.Interleave`, and the `cycle_length` is required to be + equal or higher than the number of threads(`num_parallel_calls`). + `cycle_length` in tf.Dataset.Interleave will dictate how many items it will pick up to process + num_parallel_calls: (Optional.) number of thread to spawn. This must be set to `None` + or greater than 0. Also this must be less than or equal to `num_parallel_reads`. This defines + the degree of parallelism in the underlying Dataset.interleave call. reader_schema: (Optional.) A `tf.string` scalar representing the reader schema or None. - + deterministic: (Optional.) A boolean controlling whether determinism should be traded for performance by + allowing elements to be produced out of order. Defaults to `True` + block_length: Sets the number of output on the output tensor. Defaults to 1 Raises: TypeError: If any argument does not have the expected type. ValueError: If any argument does not have the expected shape. """ + _require( + num_parallel_calls is None + or num_parallel_calls == tf.data.experimental.AUTOTUNE + or num_parallel_calls > 0, + f"num_parallel_calls: {num_parallel_calls} must be set to None, " + f"tf.data.experimental.AUTOTUNE, or greater than 0", + ) + if num_parallel_calls is not None: + _require( + num_parallel_reads is not None + and ( + num_parallel_reads >= num_parallel_calls + or num_parallel_reads == tf.data.experimental.AUTOTUNE + ), + f"num_parallel_reads: {num_parallel_reads} must be greater than or equal to " + f"num_parallel_calls: {num_parallel_calls} or set to tf.data.experimental.AUTOTUNE", + ) + filenames = _create_or_validate_filenames_dataset(filenames) self._filenames = filenames self._buffer_size = buffer_size self._num_parallel_reads = num_parallel_reads + self._num_parallel_calls = num_parallel_calls self._reader_schema = reader_schema + self._block_length = block_length - def creator_fn(filename): - return _AvroRecordDataset(filename, buffer_size, reader_schema) + def read_multiple_files(filenames): + return _AvroRecordDataset(filenames, buffer_size, reader_schema) - self._impl = _create_dataset_reader(creator_fn, filenames, num_parallel_reads) + self._impl = _create_dataset_reader( + read_multiple_files, + filenames, + cycle_length=num_parallel_reads, + num_parallel_calls=num_parallel_calls, + deterministic=deterministic, + block_length=block_length, + ) variant_tensor = self._impl._variant_tensor # pylint: disable=protected-access super().__init__(variant_tensor) @@ -171,13 +272,17 @@ def _clone( filenames=None, buffer_size=None, num_parallel_reads=None, + num_parallel_calls=None, reader_schema=None, + block_length=None, ): return AvroRecordDataset( filenames or self._filenames, buffer_size or self._buffer_size, num_parallel_reads or self._num_parallel_reads, + num_parallel_calls or self._num_parallel_calls, reader_schema or self._reader_schema, + block_length or self._block_length, ) def _inputs(self): diff --git a/tensorflow_io/core/python/experimental/make_avro_record_dataset.py b/tensorflow_io/core/python/experimental/make_avro_record_dataset.py index 11934175b..af4eefa61 100644 --- a/tensorflow_io/core/python/experimental/make_avro_record_dataset.py +++ b/tensorflow_io/core/python/experimental/make_avro_record_dataset.py @@ -37,60 +37,41 @@ def make_avro_record_dataset( shuffle_seed=None, prefetch_buffer_size=tf.data.experimental.AUTOTUNE, num_parallel_reads=None, - num_parallel_parser_calls=None, drop_final_batch=False, ): """Reads and (optionally) parses avro files into a dataset. - Provides common functionality such as batching, optional parsing, shuffling, and performing defaults. - Args: file_pattern: List of files or patterns of avro file paths. See `tf.io.gfile.glob` for pattern rules. - features: A map of feature names mapped to feature information. - batch_size: An int representing the number of records to combine in a single batch. - reader_schema: The reader schema. - reader_buffer_size: (Optional.) An int specifying the readers buffer size in By. If None (the default) will use the default value from AvroRecordDataset. - num_epochs: (Optional.) An int specifying the number of times this dataset is repeated. If None (the default), cycles through the dataset forever. If set to None drops final batch. - shuffle: (Optional.) A bool that indicates whether the input should be shuffled. Defaults to `True`. - shuffle_buffer_size: (Optional.) Buffer size to use for shuffling. A large buffer size ensures better shuffling, but increases memory usage and startup time. If not provided assumes default value of 10,000 records. Note that the shuffle size is measured in records. - shuffle_seed: (Optional.) Randomization seed to use for shuffling. By default uses a pseudo-random seed. - prefetch_buffer_size: (Optional.) An int specifying the number of feature batches to prefetch for performance improvement. Defaults to auto-tune. Set to 0 to disable prefetching. - - num_parallel_reads: (Optional.) Number of threads used to read - records from files. By default or if set to a value >1, the - results will be interleaved. - - num_parallel_parser_calls: (Optional.) Number of parallel - records to parse in parallel. Defaults to an automatic selection. - + num_parallel_reads: (Optional.) Number of parallel + records to parse in parallel. Defaults to None(no parallelization). drop_final_batch: (Optional.) Whether the last batch should be dropped in case its size is smaller than `batch_size`; the default behavior is not to drop the smaller batch. - Returns: A dataset, where each element matches the output of `parser_fn` except it will have an additional leading `batch-size` dimension, @@ -99,20 +80,15 @@ def make_avro_record_dataset( """ files = tf.data.Dataset.list_files(file_pattern, shuffle=shuffle, seed=shuffle_seed) - if num_parallel_reads is None: - # Note: We considered auto-tuning this value, but there is a concern - # that this affects the mixing of records from different files, which - # could affect training convergence/accuracy, so we are defaulting to - # a constant for now. - num_parallel_reads = 24 - if reader_buffer_size is None: reader_buffer_size = 1024 * 1024 - + num_parallel_calls = num_parallel_reads dataset = AvroRecordDataset( files, buffer_size=reader_buffer_size, num_parallel_reads=num_parallel_reads, + num_parallel_calls=num_parallel_calls, + block_length=num_parallel_calls, reader_schema=reader_schema, ) @@ -131,14 +107,11 @@ def make_avro_record_dataset( dataset = dataset.batch(batch_size, drop_remainder=drop_final_batch) - if num_parallel_parser_calls is None: - num_parallel_parser_calls = tf.data.experimental.AUTOTUNE - dataset = dataset.map( lambda data: parse_avro( serialized=data, reader_schema=reader_schema, features=features ), - num_parallel_calls=num_parallel_parser_calls, + num_parallel_calls=num_parallel_calls, ) if prefetch_buffer_size == 0: diff --git a/tests/test_parse_avro_eager.py b/tests/test_parse_avro_eager.py index fc4220ad1..83d39e7da 100644 --- a/tests/test_parse_avro_eager.py +++ b/tests/test_parse_avro_eager.py @@ -246,6 +246,63 @@ def _load_records_as_tensors(filenames, schema): ), ) + def test_inval_num_parallel_calls(self): + """test_inval_num_parallel_calls + This function tests that value errors are raised upon + the passing of invalid values for num_parallel_calls which + includes zero values and values greater than num_parallel_reads + """ + + NUM_PARALLEL_READS = 1 + NUM_PARALLEL_CALLS_ZERO = 0 + NUM_PARALLEL_CALLS_GREATER = 2 + + writer_schema = """{ + "type": "record", + "name": "dataTypes", + "fields": [ + { + "name":"index", + "type":"int" + }, + { + "name":"string_value", + "type":"string" + } + ]}""" + + record_data = [ + {"index": 0, "string_value": ""}, + {"index": 1, "string_value": "SpecialChars@!#$%^&*()-_=+{}[]|/`~\\'?"}, + { + "index": 2, + "string_value": "ABCDEFGHIJKLMNOPQRSTUVW" + + "Zabcdefghijklmnopqrstuvwz0123456789", + }, + ] + + filenames = AvroRecordDatasetTest._setup_files( + writer_schema=writer_schema, records=record_data + ) + + with pytest.raises(ValueError): + + dataset_a = tfio.experimental.columnar.AvroRecordDataset( + filenames=filenames, + num_parallel_reads=NUM_PARALLEL_READS, + num_parallel_calls=NUM_PARALLEL_CALLS_ZERO, + reader_schema="reader_schema", + ) + + with pytest.raises(ValueError): + + dataset_b = tfio.experimental.columnar.AvroRecordDataset( + filenames=filenames, + num_parallel_reads=NUM_PARALLEL_READS, + num_parallel_calls=NUM_PARALLEL_CALLS_GREATER, + reader_schema="reader_schema", + ) + def _test_pass_dataset(self, writer_schema, record_data, **kwargs): """test_pass_dataset""" filenames = AvroRecordDatasetTest._setup_files( From 0663d3868e90e9a72f9546ccc96ea3d0aa5442e8 Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Thu, 7 Jan 2021 01:28:03 -0800 Subject: [PATCH 31/85] Fix incomplete row reading issue in parquet files (#1262) This PR tries to address the issue raised in 1254 where reading parquet files will results in `InvalidArgumentError: null value in column` The issue comes from the fact that parquet's ColumnReader C++ API `ReadBatch(...)` does not necessarily respect the number of rows requested and may return less instead. This PR fixes 1254. Signed-off-by: Yong Tang --- tensorflow_io/core/kernels/parquet_kernels.cc | 52 ++++++++++++------ ...47e1-925c-9b42c8716c84-c000.snappy.parquet | Bin 0 -> 1009272 bytes tests/test_parquet_eager.py | 21 +++++++ 3 files changed, 56 insertions(+), 17 deletions(-) create mode 100644 tests/test_parquet/part-00000-ca0e89bf-ccd7-47e1-925c-9b42c8716c84-c000.snappy.parquet diff --git a/tensorflow_io/core/kernels/parquet_kernels.cc b/tensorflow_io/core/kernels/parquet_kernels.cc index c2eafb2c3..56224c643 100644 --- a/tensorflow_io/core/kernels/parquet_kernels.cc +++ b/tensorflow_io/core/kernels/parquet_kernels.cc @@ -162,6 +162,9 @@ class ParquetReadableResource : public ResourceBase { row_group_reader->Column(column_index); // buffer to fill location is value.data()[row_to_read_start - start] + // Note: ReadBatch may not be able to read the elements requested + // (row_to_read_count) in one shot, as such we use while loop of + // `while (row_left > 0) {...}` to read until complete. #define PARQUET_PROCESS_TYPE(ptype, type) \ { \ @@ -172,11 +175,16 @@ class ParquetReadableResource : public ResourceBase { } \ ptype::c_type* value_p = (ptype::c_type*)(void*)(&( \ value->flat().data()[row_to_read_start - element_start])); \ - int64_t values_read; \ - int64_t levels_read = reader->ReadBatch(row_to_read_count, nullptr, \ - nullptr, value_p, &values_read); \ - if (!(levels_read == values_read && levels_read == row_to_read_count)) { \ - return errors::InvalidArgument("null value in column: ", column); \ + int64_t row_left = row_to_read_count; \ + while (row_left > 0) { \ + int64_t values_read; \ + int64_t levels_read = reader->ReadBatch( \ + row_left, nullptr, nullptr, &value_p[row_to_read_count - row_left], \ + &values_read); \ + if (!(levels_read == values_read && levels_read > 0)) { \ + return errors::InvalidArgument("null value in column: ", column); \ + } \ + row_left -= levels_read; \ } \ } @@ -189,13 +197,18 @@ class ParquetReadableResource : public ResourceBase { } \ std::unique_ptr value_p( \ new ptype::c_type[row_to_read_count]); \ - int64_t values_read; \ - int64_t levels_read = reader->ReadBatch( \ - row_to_read_count, nullptr, nullptr, value_p.get(), &values_read); \ - if (!(levels_read == values_read && levels_read == row_to_read_count)) { \ - return errors::InvalidArgument("null value in column: ", column); \ + int64_t row_left = row_to_read_count; \ + while (row_left > 0) { \ + int64_t values_read; \ + int64_t levels_read = reader->ReadBatch( \ + row_left, nullptr, nullptr, \ + &value_p.get()[row_to_read_count - row_left], &values_read); \ + if (!(levels_read == values_read && levels_read > 0)) { \ + return errors::InvalidArgument("null value in column: ", column); \ + } \ + row_left -= levels_read; \ } \ - for (int64_t index = 0; index < values_read; index++) { \ + for (int64_t index = 0; index < row_to_read_count; index++) { \ value->flat()(row_to_read_start - element_start + index) = \ ByteArrayToString(value_p[index]); \ } \ @@ -210,13 +223,18 @@ class ParquetReadableResource : public ResourceBase { } \ std::unique_ptr value_p( \ new ptype::c_type[row_to_read_count]); \ - int64_t values_read; \ - int64_t levels_read = reader->ReadBatch( \ - row_to_read_count, nullptr, nullptr, value_p.get(), &values_read); \ - if (!(levels_read == values_read && levels_read == row_to_read_count)) { \ - return errors::InvalidArgument("null value in column: ", column); \ + int64_t row_left = row_to_read_count; \ + while (row_left > 0) { \ + int64_t values_read; \ + int64_t levels_read = reader->ReadBatch( \ + row_left, nullptr, nullptr, \ + &value_p.get()[row_to_read_count - row_left], &values_read); \ + if (!(levels_read == values_read && levels_read > 0)) { \ + return errors::InvalidArgument("null value in column: ", column); \ + } \ + row_left -= levels_read; \ } \ - for (int64_t index = 0; index < values_read; index++) { \ + for (int64_t index = 0; index < row_to_read_count; index++) { \ value->flat()(row_to_read_start - element_start + index) = \ string((const char*)value_p[index].ptr, len); \ } \ diff --git a/tests/test_parquet/part-00000-ca0e89bf-ccd7-47e1-925c-9b42c8716c84-c000.snappy.parquet b/tests/test_parquet/part-00000-ca0e89bf-ccd7-47e1-925c-9b42c8716c84-c000.snappy.parquet new file mode 100644 index 0000000000000000000000000000000000000000..a1eef8197d86331d82496e1be6c5cbedbb8fb16b GIT binary patch literal 1009272 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

Mz*R$bN`yP$PiG|dO(717nJ%J!P6T@y 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

2U9B3HdH*|kbY+uD0w07 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

(3VS)wc{i92IYzkoV&bRP_} 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°qzu{bt`9oG`f3I8|)KSmA$L=^(f`+Ppi~8QM~e~ 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�XjNR>y=%NK1W;rdJxCKlS5LuZL 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

    &aCgL;E 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�w@4n$j<_Iu=BztKt1fk^f`fD2m1#I0j7^8~P>SHumB@ zY4(fD?=eIkNV6ACf|a|{?9c8@vo8{pW?wMd2t&Br2m2>`)9kaOJ+Uv%K7srr*gyLS z!`vNzbcWq&_U_SX_SHen{b}|q(5Su|y5;oTMBEu|D{`8t8RC=lmt7Oyg#QS(MALwo z>hV{+Qx zm)+)&E5!XutDJPo&$;1fNX&d5FOOLNeh;Ey^-8OnW}naW!9IJ1J%*RAAME>P>R+&< zp4~z6dUy1knp94+|L>ol_I4S=4ZWz}xIO=GE|Pb(;ZV6fhVJyRsFPrrPCfwiSUuIS z?8r^SCYNzeon6N{y{+BWIA+oyEdc;GSC%x@w541|G8_}$uwO6??O2<7Nqr%x{9RC4XuzdSj`T!?LEc7b(e zjd|!JzZN@bw`g`(Z79(BZ>}wCw2Z50{JPb!Xws zu1US@Gn`2?j%_My^o{y+U(@klv#a)zW*qgp^Y+x}3@>@c*SM>6YL8%Rc1zBr`xsEt z@VmEm(X-TZ(EN+k&Y79ZsNT{|o|DTMN3XJQZDM){mhcYfOnUB`Tb!Et@Q<)uMmKut zUR~(t%(JLOt&uvNRd=BK)U5Z^e?_;DyVE^wYF_4d|2oWW8p_?F(=*buDZ`mg@%+XU z|Cd?k)|534q-QXi;^g&SYliBsqSgItP7pCYC*6Btd_5=i%eo)gft*M8ran5qY}E;R zMp@5RdsXD#wYOOJpuA@3y>lk68S10FzK07d{_Lq|tMe~rH$BHw>|us4Z6ogTU(S}B z=a4+(&wc7HD-zc%y}u=Za{7*!F-u%%&Yy2Z-h=4h-+(Mz?~i_? z^!@5v(f6u%TYLpPWuE%S@t*6qPW$RR)w%S3=snSW>VEZ1<+o;z?o+m&gPx7n_15o{;(DhY@pTT}=OT91JE=2ho}RbvQqMr&i0)VKL=Eo2i934BZ-wSv zD`7AeAg}LJzq`6G{T_B@hMuo}5A}WNH&1nf_R;t2$urRJQgz)EwtA_LcF^x*Y3?bB zd(l4HQNN@5M(=WGdVZa#)o+`A4|a3s+C#s$dS7%N&D1-so_a=lXZ2g4-L(5DbV1bU zdHNC4tVP)B-#>`oVPg6_qFtodZ>@eq>T+-T?S4W(?Nf%hdg$BMyE61h#oxb``u)-F z+T}dYT{U{I)kp7$KYf#!KNvp9dDL5Xq4zcx*8Ahfd!XND{k;i6tM5R&1aUXIFXc5u zHF_`fjp+BM*7~x)pM6R0L*IwqZ+-u|$8GGW_f@|!I*a~Z>GxUZ*WF03Z(tJh^c$tS z)3>Ylwh6oHUC zb4X@4y{md&`d0O~Lf?k=ZpI$^`(A~f`n^+)=IcGuJFEHnTUe4h{eJ74+)O`x+xl*_ zm)>XXBfai<0nb;z3wrO=*8^xz{Z8pw>OIllB7MJl*DKJ^l^J@c^?O$TXvN=W)3dCD zUf-kcP=BxWed(O~J=Wg|J$LP*@2w%W?p1RJlGES6IC|(eK<}_@6Lx@EPrbLgGd&OG^?T#V zZ_rm}=${SrJ+(rkf5y^t(cg3ZuIir$^e*cC^JX_&W?o_bFIdk^ccoY`J#+^nxr?bA z_}0OdqK4mpYnEf{-gSQMpx-q;3;m|+xju=mXzWanN9eXrto?hgFSh

    9<4ASN&{3 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 z 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�t5>_k{DuI@jeQ{%ThL4~;A3 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≧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 Date: Fri, 8 Jan 2021 02:08:53 +0530 Subject: [PATCH 32/85] Tests to train a keras model using MongoDBIODataset (#1264) --- tensorflow_io/core/kernels/mongodb_kernels.cc | 16 +-- tests/test_mongodb_eager.py | 101 ++++++++++++++++-- 2 files changed, 99 insertions(+), 18 deletions(-) diff --git a/tensorflow_io/core/kernels/mongodb_kernels.cc b/tensorflow_io/core/kernels/mongodb_kernels.cc index cc342ab27..cc4356c21 100644 --- a/tensorflow_io/core/kernels/mongodb_kernels.cc +++ b/tensorflow_io/core/kernels/mongodb_kernels.cc @@ -59,7 +59,6 @@ class MongoDBReadableResource : public ResourceBase { // Register the application name so we can track it in the profile logs // on the server. This can also be done from the URI. - mongoc_client_set_appname(client_obj_, "tfio-mongo-read"); // Get a handle on the database "db_name" and collection "coll_name" @@ -87,12 +86,15 @@ class MongoDBReadableResource : public ResourceBase { const bson_t* doc; int num_records = 0; - while (mongoc_cursor_next(cursor_obj_, &doc) && - num_records < max_num_records) { - char* record = bson_as_canonical_extended_json(doc, NULL); - records.emplace_back(record); - num_records++; - bson_free(record); + while (num_records < max_num_records) { + if (mongoc_cursor_next(cursor_obj_, &doc)) { + char* record = bson_as_canonical_extended_json(doc, NULL); + records.emplace_back(record); + num_records++; + bson_free(record); + } else { + break; + } } TensorShape shape({static_cast(records.size())}); diff --git a/tests/test_mongodb_eager.py b/tests/test_mongodb_eager.py index b8d0688ee..ce1c7c8db 100644 --- a/tests/test_mongodb_eager.py +++ b/tests/test_mongodb_eager.py @@ -15,22 +15,44 @@ """Tests for the mongodb datasets""" -from datetime import datetime -import time -import json -import pytest import socket -import requests +import pytest import tensorflow as tf from tensorflow import feature_column from tensorflow.keras import layers import tensorflow_io as tfio # COMMON VARIABLES -TIMESTAMP_PATTERN = "%Y-%m-%dT%H:%M:%S.%fZ" URI = "mongodb://mongoadmin:default_password@localhost:27017" DATABASE = "tfiodb" COLLECTION = "test" +RECORDS = [ + { + "name": "person1", + "gender": "Male", + "age": 20, + "fare": 80.52, + "vip": False, + "survived": 1, + }, + { + "name": "person2", + "gender": "Female", + "age": 20, + "fare": 40.88, + "vip": True, + "survived": 0, + }, +] * 1000 +SPECS = { + "name": tf.TensorSpec(tf.TensorShape([]), tf.string), + "gender": tf.TensorSpec(tf.TensorShape([]), tf.string), + "age": tf.TensorSpec(tf.TensorShape([]), tf.int32), + "fare": tf.TensorSpec(tf.TensorShape([]), tf.float64), + "vip": tf.TensorSpec(tf.TensorShape([]), tf.bool), + "survived": tf.TensorSpec(tf.TensorShape([]), tf.int64), +} +BATCH_SIZE = 32 def is_container_running(): @@ -53,10 +75,9 @@ def test_writer_write(): writer = tfio.experimental.mongodb.MongoDBWriter( uri=URI, database=DATABASE, collection=COLLECTION ) - timestamp = datetime.utcnow().strftime(TIMESTAMP_PATTERN) - for i in range(1000): - data = {"timestamp": timestamp, "key{}".format(i): "value{}".format(i)} - writer.write(data) + + for record in RECORDS: + writer.write(record) @pytest.mark.skipif(not is_container_running(), reason="The container is not running") @@ -69,7 +90,65 @@ def test_dataset_read(): count = 0 for d in dataset: count += 1 - assert count == 1000 + assert count == len(RECORDS) + + +@pytest.mark.skipif(not is_container_running(), reason="The container is not running") +def test_train_model(): + """Test the dataset by training a tf.keras model""" + + dataset = tfio.experimental.mongodb.MongoDBIODataset( + uri=URI, database=DATABASE, collection=COLLECTION + ) + dataset = dataset.map( + lambda x: tfio.experimental.serialization.decode_json(x, specs=SPECS) + ) + dataset = dataset.map(lambda v: (v, v.pop("survived"))) + dataset = dataset.batch(BATCH_SIZE) + + assert issubclass(type(dataset), tf.data.Dataset) + + feature_columns = [] + + # Numeric column + fare_column = feature_column.numeric_column("fare") + feature_columns.append(fare_column) + + # Bucketized column + age = feature_column.numeric_column("age") + age_buckets = feature_column.bucketized_column(age, boundaries=[10, 30]) + feature_columns.append(age_buckets) + + # Categorical column + gender = feature_column.categorical_column_with_vocabulary_list( + "gender", ["Male", "Female"] + ) + gender_indicator = feature_column.indicator_column(gender) + feature_columns.append(gender_indicator) + + # Convert the feature columns into a tf.keras layer + feature_layer = tf.keras.layers.DenseFeatures(feature_columns) + + # Build the model + model = tf.keras.Sequential( + [ + feature_layer, + layers.Dense(128, activation="relu"), + layers.Dense(128, activation="relu"), + layers.Dropout(0.1), + layers.Dense(1), + ] + ) + + # Compile the model + model.compile( + optimizer="adam", + loss=tf.keras.losses.BinaryCrossentropy(from_logits=True), + metrics=["accuracy"], + ) + + # train the model + model.fit(dataset, epochs=5) @pytest.mark.skipif(not is_container_running(), reason="The container is not running") From cebc61370b70b90aa78b9c7c80e952e1b637aaeb Mon Sep 17 00:00:00 2001 From: Cheng Ren Date: Mon, 11 Jan 2021 11:24:49 -0800 Subject: [PATCH 33/85] add avro tutorial testing data (#1267) Co-authored-by: Cheng Ren <1428327+chengren311@users.noreply.github.com> --- docs/tutorials/avro/test.avro | Bin 0 -> 369 bytes docs/tutorials/avro/test.avsc | 1 + docs/tutorials/avro/training.avro | Bin 0 -> 180595 bytes docs/tutorials/avro/training.avsc | 1 + 4 files changed, 2 insertions(+) create mode 100644 docs/tutorials/avro/test.avro create mode 100644 docs/tutorials/avro/test.avsc create mode 100644 docs/tutorials/avro/training.avro create mode 100644 docs/tutorials/avro/training.avsc diff --git a/docs/tutorials/avro/test.avro b/docs/tutorials/avro/test.avro new file mode 100644 index 0000000000000000000000000000000000000000..35f63a6b6239d4cd8c4e60a29ac226538128f5a8 GIT binary patch literal 369 zcmeZI%3@>@Nh~YM*GtY%NloU+E6vFf1M`cMGg5OC=dn~Pl~fj_Dp@Hg6{RNU7o{la zC@AG6=7L2$a}(23T@p(Yi&INL;%S+wIVr_Jwb5{0aE4N1QBh(gNL6M@YA#5TQf6L> zQZ15kX{m`NrA4X5AVIjkXs|MnDxlMpVv&^RBqpWips0mwQcBG$%|&);3eb@uKz|g2 z1dA(klk#)G?o|xY mkGp+k=KR+j7F-Mr3``shObjeQ29RI^l59*&K#GG2T{QsBCUl?x literal 0 HcmV?d00001 diff --git a/docs/tutorials/avro/test.avsc b/docs/tutorials/avro/test.avsc new file mode 100644 index 000000000..904864b37 --- /dev/null +++ b/docs/tutorials/avro/test.avsc @@ -0,0 +1 @@ +{"name": "ImageDataset", "type": "record", "fields": [{"name": "features", "type": {"type": "array", "items": "int"}}, {"name": "label", "type": ["int", "null"]}, {"name": "dataType", "type": {"type": "enum", "name": "dataTypes", "symbols": ["TRAINING", "VALIDATION"]}}]} \ No newline at end of file diff --git a/docs/tutorials/avro/training.avro b/docs/tutorials/avro/training.avro new file mode 100644 index 0000000000000000000000000000000000000000..8608de4c472a628fe44721730c794d8bdb587d1b GIT binary patch literal 180595 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 literal 0 HcmV?d00001 diff --git a/docs/tutorials/avro/training.avsc b/docs/tutorials/avro/training.avsc new file mode 100644 index 000000000..31ce0aef4 --- /dev/null +++ b/docs/tutorials/avro/training.avsc @@ -0,0 +1 @@ +{"name": "ImageDataset", "type": "record", "fields": [{"name": "features", "type": {"type": "array", "items": "int"}}, {"name": "label", "type": "int"}, {"name": "dataType", "type": {"type": "enum", "name": "dataTypes", "symbols": ["TRAINING", "VALIDATION"]}}]} \ No newline at end of file From fc8d472427109286676627ae5c79b416a66ac8c6 Mon Sep 17 00:00:00 2001 From: Dale Lane Date: Tue, 12 Jan 2021 13:22:04 +0000 Subject: [PATCH 34/85] Update Kafka tutorial to work with Apache Kafka (#1266) * Update Kafka tutorial to work with Apache Kafka Minor update to the Kafka tutorial to remove the dependency on Confluent's distribution of Kafka, and instead work with vanilla Apache Kafka. Signed-off-by: Dale Lane * Address review comments Remove redundant pip install commands Signed-off-by: Dale Lane --- docs/tutorials/kafka.ipynb | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/docs/tutorials/kafka.ipynb b/docs/tutorials/kafka.ipynb index 51c0ee9a7..07d8b2ce2 100644 --- a/docs/tutorials/kafka.ipynb +++ b/docs/tutorials/kafka.ipynb @@ -70,11 +70,13 @@ "source": [ "## Overview\n", "\n", - "This tutorial focuses on streaming data from a [Kafka](https://docs.confluent.io/current/getting-started.html) cluster into a `tf.data.Dataset` which is then used in conjunction with `tf.keras` for training and inference.\n", + "This tutorial focuses on streaming data from a [Kafka](https://kafka.apache.org/quickstart) cluster into a `tf.data.Dataset` which is then used in conjunction with `tf.keras` for training and inference.\n", "\n", "Kafka is primarily a distributed event-streaming platform which provides scalable and fault-tolerant streaming data across data pipelines. It is an essential technical component of a plethora of major enterprises where mission-critical data delivery is a primary requirement.\n", "\n", - "**NOTE:** A basic understanding of the [kafka components](https://docs.confluent.io/current/kafka/introduction.html) will help you in following the tutorial with ease." + "**NOTE:** A basic understanding of the [kafka components](https://kafka.apache.org/documentation/#intro_concepts_and_terms) will help you in following the tutorial with ease.", + "\n", + "**NOTE:** A Java runtime environment is required to run this tutorial." ] }, { @@ -180,8 +182,8 @@ }, "outputs": [], "source": [ - "!curl -sSOL http://packages.confluent.io/archive/5.4/confluent-community-5.4.1-2.12.tar.gz\n", - "!tar -xzf confluent-community-5.4.1-2.12.tar.gz" + "!curl -sSOL https://downloads.apache.org/kafka/2.7.0/kafka_2.13-2.7.0.tgz\n", + "!tar -xzf kafka_2.13-2.7.0.tgz" ] }, { @@ -190,7 +192,7 @@ "id": "vAzfu_WiEs4F" }, "source": [ - "Using the default configurations (provided by the confluent package) for spinning up the instances." + "Using the default configurations (provided by Apache Kafka) for spinning up the instances." ] }, { @@ -201,8 +203,8 @@ }, "outputs": [], "source": [ - "!cd confluent-5.4.1 && bin/zookeeper-server-start -daemon etc/kafka/zookeeper.properties\n", - "!cd confluent-5.4.1 && bin/kafka-server-start -daemon etc/kafka/server.properties\n", + "!./kafka_2.13-2.7.0/bin/zookeeper-server-start.sh -daemon ./kafka_2.13-2.7.0/config/zookeeper.properties\n", + "!./kafka_2.13-2.7.0/bin/kafka-server-start.sh -daemon ./kafka_2.13-2.7.0/config/server.properties\n", "!echo \"Waiting for 10 secs until kafka and zookeeper services are up and running\"\n", "!sleep 10\n" ] @@ -247,8 +249,8 @@ }, "outputs": [], "source": [ - "!confluent-5.4.1/bin/kafka-topics --create --zookeeper 127.0.0.1:2181 --replication-factor 1 --partitions 1 --topic susy-train\n", - "!confluent-5.4.1/bin/kafka-topics --create --zookeeper 127.0.0.1:2181 --replication-factor 1 --partitions 2 --topic susy-test\n" + "!./kafka_2.13-2.7.0/bin/kafka-topics.sh --create --bootstrap-server 127.0.0.1:9092 --replication-factor 1 --partitions 1 --topic susy-train\n", + "!./kafka_2.13-2.7.0/bin/kafka-topics.sh --create --bootstrap-server 127.0.0.1:9092 --replication-factor 1 --partitions 2 --topic susy-test\n" ] }, { @@ -268,8 +270,8 @@ }, "outputs": [], "source": [ - "!confluent-5.4.1/bin/kafka-topics --bootstrap-server 127.0.0.1:9092 --describe --topic susy-train\n", - "!confluent-5.4.1/bin/kafka-topics --bootstrap-server 127.0.0.1:9092 --describe --topic susy-test\n" + "!./kafka_2.13-2.7.0/bin/kafka-topics.sh --describe --bootstrap-server 127.0.0.1:9092 --topic susy-train\n", + "!./kafka_2.13-2.7.0/bin/kafka-topics.sh --describe --bootstrap-server 127.0.0.1:9092 --topic susy-test\n" ] }, { @@ -720,7 +722,7 @@ }, "outputs": [], "source": [ - "!confluent-5.4.1/bin/kafka-consumer-groups --bootstrap-server 127.0.0.1:9092 --describe --group testcg\n" + "!./kafka_2.13-2.7.0/bin/kafka-consumer-groups.sh --bootstrap-server 127.0.0.1:9092 --describe --group testcg\n" ] }, { From 5df32c69621b514320d21b137229f71f744410ba Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Wed, 13 Jan 2021 23:58:24 -0800 Subject: [PATCH 35/85] Update pulsar download link. (#1270) This PR updates pulsar download link as old link does not work anymore. Signed-off-by: Yong Tang --- tests/test_pulsar/pulsar_test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_pulsar/pulsar_test.sh b/tests/test_pulsar/pulsar_test.sh index 6abe01b75..ff0bfa357 100644 --- a/tests/test_pulsar/pulsar_test.sh +++ b/tests/test_pulsar/pulsar_test.sh @@ -22,7 +22,7 @@ TAR_FILE="apache-pulsar-${VERSION}-bin.tar.gz" echo "Downloading pulsar ${VERSION}" if [[ ! -f ${TAR_FILE} ]]; then - curl -sSOL "https://downloads.apache.org/pulsar/pulsar-${VERSION}/${TAR_FILE}" + curl -sSOL "https://archive.apache.org/dist/pulsar/pulsar-${VERSION}/${TAR_FILE}" fi tar -xzf ${TAR_FILE} From 2bbdd40713b084bac2dc1fb3d8e76e05972561c0 Mon Sep 17 00:00:00 2001 From: Vignesh Kothapalli Date: Thu, 14 Jan 2021 22:07:43 +0530 Subject: [PATCH 36/85] add github workflow for performance benchmarking (#1269) * add github workflow for performance benchmarking * add github-action-benchmark step --- .github/workflows/benchmarks.yml | 78 ++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 .github/workflows/benchmarks.yml diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml new file mode 100644 index 000000000..c17819012 --- /dev/null +++ b/.github/workflows/benchmarks.yml @@ -0,0 +1,78 @@ +name: API Performance Benchmarks + +on: + push: + branches: + - master + schedule: + - cron: "0 12 * * *" + +jobs: + + macos: + name: macOS ${{ matrix.python }} + ${{ matrix.version }} + runs-on: macos-latest + strategy: + fail-fast: false + matrix: + python: ['3.8'] + version: ['tensorflow==2.4.0:tensorflow-io-nightly', 'tensorflow==2.4.0:tensorflow-io'] + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python }} + - name: Setup macOS + run: | + set -x -e + python -m pip install -U wheel setuptools + python --version + - name: Test macOS + run: | + set -x -e + python --version + df -h + rm -rf tensorflow_io + echo ${{ matrix.version }} | awk -F: '{print $1}' | xargs python -m pip install -U + echo ${{ matrix.version }} | awk -F: '{print $2}' | xargs python -m pip install --no-deps -U + python -m pip install pytest-benchmark scikit-image + python -m pip freeze + python -c 'import tensorflow as tf; print(tf.version.VERSION)' + python -c 'import tensorflow_io as tfio; print(tfio.version.VERSION)' + python -m pytest --benchmark-only -v --import-mode=append $(find . -type f \( -iname "test_*_eager.py" ! \( -iname "test_bigquery_eager.py" \) \)) + + linux: + name: Linux ${{ matrix.python }} + ${{ matrix.version }} + runs-on: ubuntu-20.04 + strategy: + fail-fast: false + matrix: + python: ['3.8'] + version: ['tensorflow==2.4.0:tensorflow-io-nightly', 'tensorflow==2.4.0:tensorflow-io'] + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python }} + - name: Setup Linux + run: | + set -x -e + bash -x -e .github/workflows/build.space.sh + - name: Test Linux + run: | + set -x -e + python --version + df -h + rm -rf tensorflow_io + echo ${{ matrix.version }} | awk -F: '{print $1}' | xargs python -m pip install -U + echo ${{ matrix.version }} | awk -F: '{print $2}' | xargs python -m pip install --no-deps -U + python -m pip install pytest-benchmark scikit-image + python -m pip freeze + python -c 'import tensorflow as tf; print(tf.version.VERSION)' + python -c 'import tensorflow_io as tfio; print(tfio.version.VERSION)' + python -m pytest --benchmark-only --benchmark-json benchmark.json -v --import-mode=append $(find . -type f \( -iname "test_*_eager.py" ! \( -iname "test_bigquery_eager.py" \) \)) + - name: Store benchmark result + uses: rhysd/github-action-benchmark@v1 + with: + tool: 'pytest' + output-file-path: benchmark.json \ No newline at end of file From f8efb106f423334ea43c973d38fdccbb8cda656e Mon Sep 17 00:00:00 2001 From: Vignesh Kothapalli Date: Tue, 19 Jan 2021 02:17:47 +0530 Subject: [PATCH 37/85] handle missing dependencies while benchmarking (#1271) * handle missing dependencies while benchmarking * setup test_sql * job name change * set auto-push to true * remove auto-push * add personal access token * use alternate method to push to gh-pages * add name to the action * use different id * modify creds * use github_token * change repo name * set auto-push * set origin and push results * set env * use PERSONAL_GITHUB_TOKEN * use push changes action * use github.head_ref to push the changes * try using fetch-depth * modify branch name * use alternative push approach * git switch - * test by merging with forked master --- .github/workflows/benchmarks.yml | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index c17819012..bff8936b0 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -27,7 +27,7 @@ jobs: set -x -e python -m pip install -U wheel setuptools python --version - - name: Test macOS + - name: Benchmark on macOS run: | set -x -e python --version @@ -35,7 +35,7 @@ jobs: rm -rf tensorflow_io echo ${{ matrix.version }} | awk -F: '{print $1}' | xargs python -m pip install -U echo ${{ matrix.version }} | awk -F: '{print $2}' | xargs python -m pip install --no-deps -U - python -m pip install pytest-benchmark scikit-image + python -m pip install -q scikit-image pytest pytest-benchmark boto3 fastavro avro-python3 scikit-image pandas pyarrow==2.0.0 google-cloud-pubsub==2.1.0 google-cloud-bigquery-storage==1.1.0 google-cloud-bigquery==2.3.1 google-cloud-storage==1.32.0 python -m pip freeze python -c 'import tensorflow as tf; print(tf.version.VERSION)' python -c 'import tensorflow_io as tfio; print(tfio.version.VERSION)' @@ -51,6 +51,8 @@ jobs: version: ['tensorflow==2.4.0:tensorflow-io-nightly', 'tensorflow==2.4.0:tensorflow-io'] steps: - uses: actions/checkout@v2 + with: + fetch-depth: 0 - uses: actions/setup-python@v1 with: python-version: ${{ matrix.python }} @@ -58,7 +60,8 @@ jobs: run: | set -x -e bash -x -e .github/workflows/build.space.sh - - name: Test Linux + bash -x -e tests/test_sql/sql_test.sh + - name: Benchmark on Linux run: | set -x -e python --version @@ -66,7 +69,7 @@ jobs: rm -rf tensorflow_io echo ${{ matrix.version }} | awk -F: '{print $1}' | xargs python -m pip install -U echo ${{ matrix.version }} | awk -F: '{print $2}' | xargs python -m pip install --no-deps -U - python -m pip install pytest-benchmark scikit-image + python -m pip install -q scikit-image pytest pytest-benchmark boto3 fastavro avro-python3 scikit-image pandas pyarrow==2.0.0 google-cloud-pubsub==2.1.0 google-cloud-bigquery-storage==1.1.0 google-cloud-bigquery==2.3.1 google-cloud-storage==1.32.0 python -m pip freeze python -c 'import tensorflow as tf; print(tf.version.VERSION)' python -c 'import tensorflow_io as tfio; print(tfio.version.VERSION)' @@ -74,5 +77,8 @@ jobs: - name: Store benchmark result uses: rhysd/github-action-benchmark@v1 with: + name: Tensorflow-IO Benchmarks tool: 'pytest' - output-file-path: benchmark.json \ No newline at end of file + output-file-path: benchmark.json + github-token: ${{ secrets.GITHUB_TOKEN }} + auto-push: true \ No newline at end of file From 544740ad9304c64766d357038013dbef5a34cc0e Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Tue, 19 Jan 2021 08:23:38 -0800 Subject: [PATCH 38/85] Disable s3 macOS for now as docker is not working on GitHub Actions for macOS (#1277) * Revert "[s3] add support for testing on macOS (#1253)" This reverts commit 81789bde99e62523ca4d9f460bb345c666902acd. Signed-off-by: Yong Tang * Update Signed-off-by: Yong Tang --- .github/workflows/api.yml | 3 --- .github/workflows/build.yml | 3 --- tests/test_aws/aws_test.sh | 11 +++++------ tests/test_aws/docker-compose.yml | 12 ------------ tests/test_s3_eager.py | 4 ++-- 5 files changed, 7 insertions(+), 26 deletions(-) delete mode 100644 tests/test_aws/docker-compose.yml diff --git a/.github/workflows/api.yml b/.github/workflows/api.yml index 4c61f586b..145d30884 100644 --- a/.github/workflows/api.yml +++ b/.github/workflows/api.yml @@ -22,7 +22,6 @@ jobs: version: ['tensorflow==2.4.0:tensorflow-io-nightly', 'tf-nightly:tensorflow-io==0.17.0', 'tf-nightly:tensorflow-io-nightly'] steps: - uses: actions/checkout@v2 - - uses: docker-practice/actions-setup-docker@v1 - uses: actions/setup-python@v1 with: python-version: ${{ matrix.python }} @@ -33,9 +32,7 @@ jobs: - name: Setup macOS run: | set -x -e - docker version bash -x -e tests/test_azure/start_azure.sh - bash -x -e tests/test_aws/aws_test.sh - name: Test macOS run: | set -x -e diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6093357a2..898100826 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -162,7 +162,6 @@ jobs: python: ['3.7', '3.8'] steps: - uses: actions/checkout@v2 - - uses: docker-practice/actions-setup-docker@v1 - uses: actions/download-artifact@v1 with: name: ${{ runner.os }}-${{ matrix.python }}-wheel @@ -177,11 +176,9 @@ jobs: - name: Setup ${{ matrix.python }} macOS run: | set -x -e - docker version bash -x -e tests/test_kafka/kafka_test.sh bash -x -e tests/test_azure/start_azure.sh bash -x -e tests/test_pubsub/pubsub_test.sh - bash -x -e tests/test_aws/aws_test.sh bash -x -e tests/test_pulsar/pulsar_test.sh bash -x -e tests/test_mongodb/mongodb_test.sh start - name: Install ${{ matrix.python }} macOS diff --git a/tests/test_aws/aws_test.sh b/tests/test_aws/aws_test.sh index 1d21494bc..a4778a48e 100755 --- a/tests/test_aws/aws_test.sh +++ b/tests/test_aws/aws_test.sh @@ -17,11 +17,10 @@ set -e set -o pipefail -export LOCALSTACK_VERSION=0.12.2 -cd tests/test_aws -TMPDIR=/private$TMPDIR docker-compose up -d -cd - -echo "Waiting for 20 secs until localstack is up and running" -sleep 20 +LOCALSTACK_VERSION=0.12.2 +docker pull localstack/localstack:$LOCALSTACK_VERSION +docker run -d --rm --net=host --name=tensorflow-io-aws localstack/localstack:$LOCALSTACK_VERSION +echo "Waiting for 10 secs until localstack is up and running" +sleep 10 echo "Localstack up" exit 0 diff --git a/tests/test_aws/docker-compose.yml b/tests/test_aws/docker-compose.yml deleted file mode 100644 index 3877f5637..000000000 --- a/tests/test_aws/docker-compose.yml +++ /dev/null @@ -1,12 +0,0 @@ -version: '2.1' -services: - localstack: - image: "localstack/localstack:${LOCALSTACK_VERSION}" - container_name: tensorflow-io-aws - ports: - - "4566:4566" - environment: - - SERVICES=${SERVICES- } - - DOCKER_HOST=unix:///var/run/docker.sock - volumes: - - "${TMPDIR:-/tmp/localstack}:/tmp/localstack" \ No newline at end of file diff --git a/tests/test_s3_eager.py b/tests/test_s3_eager.py index dc8e3ff0c..ad18af1f7 100644 --- a/tests/test_s3_eager.py +++ b/tests/test_s3_eager.py @@ -24,8 +24,8 @@ @pytest.mark.skipif( - sys.platform in ("win32"), - reason="TODO Localstack not setup properly on Windows yet", + sys.platform in ("win32", "darwin"), + reason="TODO Localstack not setup properly on macOS/Windows yet", ) def test_read_file(): """Test case for reading S3""" From b71fcb37d682b596a29c2acf6d71866882242e20 Mon Sep 17 00:00:00 2001 From: Cheng Ren Date: Tue, 19 Jan 2021 10:37:14 -0800 Subject: [PATCH 39/85] rename testing data files (#1278) --- docs/tutorials/avro/{test.avro => train.avro} | Bin docs/tutorials/avro/{test.avsc => train.avsc} | 0 docs/tutorials/avro/training.avro | Bin 180595 -> 0 bytes docs/tutorials/avro/training.avsc | 1 - 4 files changed, 1 deletion(-) rename docs/tutorials/avro/{test.avro => train.avro} (100%) rename docs/tutorials/avro/{test.avsc => train.avsc} (100%) delete mode 100644 docs/tutorials/avro/training.avro delete mode 100644 docs/tutorials/avro/training.avsc diff --git a/docs/tutorials/avro/test.avro b/docs/tutorials/avro/train.avro similarity index 100% rename from docs/tutorials/avro/test.avro rename to docs/tutorials/avro/train.avro diff --git a/docs/tutorials/avro/test.avsc b/docs/tutorials/avro/train.avsc similarity index 100% rename from docs/tutorials/avro/test.avsc rename to docs/tutorials/avro/train.avsc diff --git a/docs/tutorials/avro/training.avro b/docs/tutorials/avro/training.avro deleted file mode 100644 index 8608de4c472a628fe44721730c794d8bdb587d1b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 180595 zcmdqK4@h6fyC-~RPRL4-5JHd=iim_zLI@#*P)Z1H34L=*=#~&dwr{uO*PCD7Zph6o zx!uyu?F|w{f`|kOAuA#hB#4NJ1Q8Jt5fKq_T@eux5fKp)5fKrQ_w$)KbNO zm%mjV`1jIZ{^!qsFa4sV^ugc%`ClL0ESEw-JQSx?U(=Y;P3y& zmq|WCrGW1J?Z?00%RnTcf4~1f{v{228>C$a_5a3~|Moxs<;Q=$r>HBve(gX1(>MS0 z&HuyqH~#b0f4cmium973`KI*0rKvHQOaJ4){YS9ze_oFN*VfTPNB-Bn|Gi#0_wP00 z)~8@E|XV1T#nMGL3>r0PY>=!05uW-4hn)QB(Pc+=EB|Zt2~Coli_yQ7`DlA z*@nCN@IW4>Uu+|3JKRzKZ-=dpngp$KDSRnc#jt2n;GgF4=r{ZhQ!Y|<^4{Mem=3pe zle*B~o)1_2m^aZB>*2VIc!i7~sXjdALdDU@#g+v9f>nu%ATmq zw>*@6GK}Qu&^PbjX2DBd+`E_vcEScP;h&9%V_}h}=y&;AewaaZl@m~pJmMj}FPddL zrMo?!in(b1-3}L=jdtu=46u#P=qhIwBycLPQ~xixV1~phKFc=dvrpDBBd$1BjUKTE zfSs@-EDJ^evmFlR2)e-KWE&%{97!5z#O?~8Hz}CyaF*FM&`X;p62Yr3ERKQZt>S7_ zS3+mgv&X0!5<^*_l0k)LA#76b3%7xH!yeIRbXmii(na-2tXBG6e}=8FQ{&%N^sR+I zrQ6$7?6s~h&IDIbc!YUQA4l^bGRt}4LIaM+q_4WzEc zHkN$ejf6{Cgh6n=IVws+?u6sv6Iqt7fx0gp7b6j1w|pslGgL0$M|iQn*F|T8>|)w~ zYL5v6DZgbuTLo8l=XOz1otPC3;*d$yzu%kBZ2y*rrD@1ZJ?b0IhSO?b8%AyhZ?Ujs z2&ht>xmun+-hlGV^%VgJ(|`|`70S3nd`%K2!qnV$mBc@nhYcY+=)@Kh`RxUh8U!y? z$(cfu#2~0peSS{OCh7O){)yaD6un4OX%d4#c8L|0zA2q+bXk8iF}8-EC%KZlo6Cy4 zZE&t-$=l@aLaoA%Hn2xp1oKScpGUg&5u@}hYI;jPRaVPSAEr;@Jtrq z@VC2i4y~=nRq~q!yZj;os0{mLi>Oj9%q&CMGM^z~C!7!GJ5JX znoy63>&@6njp4=7G)RZ~#--*JAD6PL5#-|hZ$i(E;KJum<+!g}8F%O0|3;JH(aG z5+L5)>JdHS%j~3k%k7E1u zLb)BbPN=d~`5Loo4F(rP zXf~r4l?nE52h{W=V=qk%?#mXDd&rxtSGPt7ks8m0nw0%Yt!=eB-m?mg9|u8OcX~@7 zr>Gky`=#*#lXtxzF^lRAPy1pw8GO5nEs2-XAxyF}4pFJj@J#fhvkXPMyBv~(i&&~Q zLm!tDJW(rIwXgJRR1j>&qRgKx{KrOA_ISu#x6<2GH<=0_TJNLB@<<-s>-(773jIr8 zHyl^%SFexBJ;QZu!@Cd{P-49U;jf#x;1*Li!k0ecrd0OlKH~1`HO^D@Kns#Lq=&;k z-G{=lQ}q@84O4f*$G-GJ>TagOuRV49V6WdH`+1~Zl!x`gq03ga>4un&*DLo2DeS?ifhoyk8gLkcfz;*o z9TAi!KOSr8yu7E(W-Hv0z8zmPy2~E(=B)*!ZczkN+=6v#M`q;_ETLRAP*Ye`SRtkX ztFNq-HwGJ%g>zAZYPi|ZnjJK{G1;NoWjmaf%)l0Y#L|+#a@5Rl321&1HT13xcVkvn zVJjRB8$~O08M@#NS(4yVcN40q*w_>+X{gB@!yN=BjWiK@z}2`LifAI4Lf=FC{pJ1& z-z5jy;L#weR(U0-!qdL?p_bAkV`rAOaMm}nUip4@ zcwi2*MvoWq7K-)EH$Zr9CVZu<+bG&aajuAExxz7Vw+aG!@_Y-Sy`!u|-)a}Z!Af-H z38>NfLAB9wPW0TL$&+1 zC<3irSX?auZx9TDFS~-hy7&YTwU9~9=9JC^`D|E#0}WmRU`OWGpdJX8ZWRRb#2_>; zgEFrbc|aLNaYcmHS&AB%^OC;NJKAG#*_ zmm&9J$^oYt6fZ!qi5`cq=tB@pt8~v|7zFM3^lL7|HC<6A$J>)dF=SVyqzZ+{0Nt3t zpIIPZk-SKd@HOp0vU5PmI7-+t1}I}7w%lSAe`dOdDEx6*P-1I?yi_<=<;Blm$PqcK zo|wmiyUA^-xnRzRo!^X@SI`z@ooY0KK9Lnt^UVu+gaxd9Iaa}_^&KF=`O7h<;7U&Q zr#B>b=F3lI4qQ!_O20K_w?-=Qec$ePn>SDWi!L|HMl7Z52cwcxarW=`YWWmBKH-@h zRy3zKlsJVPD+Zc;EeEjDZRae>8if|(Y=_SwSu83Dlo?BoiJ_U zg)~~=87%ervXV;FDq3=@j{!KLO$262>nUpc`ZrMcaxYNwjne1@H)5VmD$c~>+8i> z>d`Pn@YI?rV}P4natB3W{Nm5t><`(?AF9F8Ixt5$W2zX{)xl`%;aMGTPGYwZZBFc_ zKCmi&3JS)>IPf=ySC?R(39)A8@5~lc-$yFR&f6iijVVkav&dT~In=nUlqNA?4o<+w-&;fG)H{x39wn zm&T#y_wBg~UZ7y_emVG#M7P=PZ^|tgR#clu`*%fE+1k;@K=+n_x-KLS?E0Gk>e(Da zl0eRhMST0@`A^<`?19!NR)m%_Zs9H`zbWh&qgXw(86(#PUNbOXvo;Q&nlx+^!+F^n zL2hG{m2IG+YP3F4Jj3qm;=^hMIGr0t&#|5Q?K@_Dr4|I3y!bjprvlglSC%cwJZ(cB zLh8mWN}}{f^J-cr#I&m`cL{m`nCf*4$3PpC^D)3^SSMoM&5(fKrMMKV#;$UL;Oru{ zWEztP0(cq~p4Z`}6z*SAH0kv1@Lno?E!>v1N$W)4Dh)xq)kQS1;rMr>Mo+_ZDC=K} z*jB_N{?!W_DnR;@<#M?DFwJHEHd{6SqEBo|%`haOV=$S@kIrhbgo{$7K@e(N0;g*G zA#M-8OR3DeBlz$)HS(1l0h93xzpv!;yy@;&6jIpNQ21?@h_Cbpk{kH-=z{nXSF1IM z%CXk2VvsM0Z-}8nZGmQOg~*(YrvdIba(k>GcrLmlbDaW8Id**hI7JhR{Ass2XK=ew zWgZ3}RG)PCxNfqvR#ItVt}PfMG?{%^3^g9_;|!eseQslH0WG~)#LV?fj4A4BtVRlC z357QJ`JfeK)2$^B$tsA$ zro~m)RO!g@r(W&T!TN{mRCFUK?i1YE&xFnLxx8`i$fkIlRYx6}L|-XKSJTs~*=-JD z+2xy=uq-KlO-=SNa+Ie|o!S+XxT8k~U4D5`8Ar6r74i)xF0oM0yy$8x&NI zg?EyS>ARzK$cuD)$QYPQZ0Av*1~({I%ki8q_Z99GhLup)AWZ!hH9*F?9nNJMCzXDV zt}BC{;SSCSl&yG;Bwu2*Ziky9YQhv+bz8`d^op&eiJ`Pwr`}gD2ncEL8nu6%Yww$n z&Fbz=oj3K^)u%8T-^IwO$jQ^0=@gl#`C00qGGSjsqaIhtqxrMVptiA zy|xB%X%BF`hRmi}>EHtR4vbr@^*Iz{mbD;wDjLEcAJSRD{GFQ$Z<_ny(`R8nFxI4t z0fM?vv!d5pMT-~}bTr_V*&!Eq$riZoRf3$?U=3)|qM`VIbIG2SJIEOI*;-Vr zLZ+qWtH-qXYM%|Rcs&6Z?2f`%gMNW^?1~c8}aywt_k3m?D zRj)kV_p-F^B6$x0oeTg^!<0j|s4ou$08|O86gQFtVAs8=N&~p7l8}LNoTacC;>!&Y zS4Gk4Y_b@Gx6RVDzL^qtqrJH{L7I^42aTRmIOLn#%nbp?*p%Ze0|rY5z}ceAq}s!q z>OuQHUcW;-xpz+9gQsXmv)`VOmEu6Ihz2o<`6+wl9Uux`M@!r$1Ud$IF6Wq9SQCL^ zErd{UUTQyhPQBTJw<9j@DfTzef}Uc4mmE~lD^*lmWVOdlpAB<8z(({HiTp4S7%p4_XhK1NIEMujn(n zun&Z_ad*k3kE84QgpY_F!3|PgE+1ie0HLnc3sU%>pu!MBy8q5_diwlWi`W!!oxl{9 z2YZbC^Kx7mEedStTtU6+!R69nZ7{q_i`Ygy=1Q$oJ+oZ`A_%^lx5fcq62j=w0pP@- z-E)suVn!)pp7dY69Om~q{KaFa66YL^!UABqesW8Z;{00V!Y8p~sYBCj$|sH{34#JG zd|JTBDNL8YpYGqK8C^5Dtn>tF)w2Pge#z0=5m+Po#k8{>32=&5UjAgnxmb5NHb$uX z@f}^?StsA!#eT>mn+%FruD4P}ElBla?o^vPpAhZ|1b`R{-YU}zxept`Q?(+c9E_@j zpIS6`Zy4My2MxT+^F2M70|l$1OVTBCSN6$T{Mr_Vns#eUiLa9nyh?%l##nD9Z zpQ17kcB+Z~m*~+25){;fp2|r0cuosTpIb}y6)M&zFu;0+zLCunn~YpZ~#)`(DGyXZLxvm-7Xc{GctASP?_`|BdO z-gCjR#~A0MVUwea44{M&*@VzI+HiLVa@Qokxf@rM^ua~G3c^-~5pmfRUATCWTJ1}D zV+n%?zmMSR?4TopSOn@tdrT*}>A9KS;Uy`&dmfI2W!YD$r{_5<;#6V=H&A6$JtQp%9ru8 zbu-Mlms`eHqREaUs6odtkQ6IP3+v7Bu7w~7e&zHQLu}^)PXxg%3YpOzlOd2MZB<(k z+~Km^#g2WDrU#C=s#?7;!>p|!Bo~R1WEE?XPaQnYo;mj)tAbrEx2HO?1wNBnb-K4wbI)NgW7|F-XFuar$4|ZyJ=ha@y?S2*WgNbH zuTm}b*mInm0MHK2N)B8b1;T0@Ah^zQl#wJtg=0wO6>?RooYO(DtfYOt4yBxq^n~!O z%OC^WDwm4+*p3=pdI|Rg11L3h-S(;2=pU`GHm7Lo0Qg-r=sn6!cinz#v|^gTq;+8o zq;5LKdIQ38O>o`(oDdE@Vp$KqUpLp9NaUPfb~t5mG#@AzLu_|fF(9!MHnCfgWW38Y zcOBgpT5?R9XN1Qk#OkSk>Tw^$bo*oUAM6n?@XdGK!EA!yt7jN(WAIXd<~Sc!3S~YR zhN;O7=-TC6UX~XG-LPVAR$Tv*=yAAFFzE3swY@7Cl8(vv&MNH23s^UXBpl06AoN3~ z@!7*kwYwe7_nA>Z^ni#dIf9b<(XGO>q*?l{B5Pk4)ML4|kn zZ5I_$OgWlQ$W~kj%xgKTSVJA+mA`s-!8krP`}wPPq@cb9l%bheVW$4c?*iL+510CF zgbFK_~}v0g~51 zG*k8EfI0XW)51FVhT+Ia=WDA{`td|?WDrQUb9FY*%4OQK00TxPL;C9eW60b^zd0wZ zhujtk3fI<7r(1}qpl4*{N|QV#R6I`g)Dp9B{%limt7t68<8SVGG?2sxo!&g zWoq72&)M5rxkpnSZ=I3*$K|*YAlVQSmuRx`w_L;sNV*G&sYmjg5TBrm zs-f$j{y&G31+sn2W$^lHNFsG5lG3a@g(6pw0WvLao~>U)x%JvSChce5s9!c*^ukoY zZav+yaW&0%$hUIl6PUI``(z5_gJ}p|UtrF5tsMM2c(EURo(eWa9fSr>j<4{EYyu;y zxuTprg$>Xy%yx%B^Es1j!c;~G2(d1Y@s<5&Kp2S86ZqjII)yk#7HA>=?IL`qxH<94 z@k%R@3dY%y{cI()dc3+KTJw|pb8MM(L+JlkARfwNdI9><*PIpnUJ?4a4m3=bwE}Jx z5Xrlwhh@*?R^CgpevG^QFeWPn zzgkW7k?dADIVX%WcHTf#0S_sup%&FGhGV(T5d^nK<#Z&=(_ppvzI=Hk13|cnoO#U= zS{L%#LWIP-glgb)V=P!L zJH294Vgm)RH-{BnQ(1LM@o3aeQ)=F7jGnBeCh|y%&gwYPrJ$BC4tJgnAyGgLp*;FYtj!< zk*JJ<2Xp}56{m~Qx9z_Y#017T)EA7$Fe$&veaWL6YN$>l{J^L(JSX^qt$)CXTts-R z?@&ZVI30dHDPGw}0v4}BFcOKA7!))oycmb6tC7GT#hA_7?f^5V4x`Q<#C_IqCjxkE zYA^p4YqBl`K%#EOi)IVWV<6)UJila8QO{0b#C2e#Vt7kqQN%~p7a|*`N7VTh=yv+JpyLl>TMBO1t+22M%o3>5*L{OYXLkwH6!PFS zrE+^Dv+(zoeEc;wU49!oP2ivX|1T?z{rwqyX6cyIpWl(7Q7%@Kx`wDQL!uE+tJYbIfbWIwfhelg0$5rOoVIiK4YJ5Mb|*4RDXPVT_2Fn$x{xTn zcCpXN&=NEtPq+0hfw(h=QFMfVge6aINB_W zgi2Q()=;x-T!?r%wglISPG3V1+w#r<72vG2c3B7gXIX{fM7&$NewBRMrYCwn%@}ZF z7UP@1>UblrC0D~>2aDZxX_6s(#}BufYl;|#$sOs*Cr(i}&7geRpF0b@*VF!89nWx% z<22X=BYwMMu}V!)@ae9i|F3d%)EBVW=D1mwGqAqV`_>UVR$+0kTmaOHq#@M3=NhSH zjCzk{m>7ts^xg?ZFR8y`2ACO(7c|LV zCFR%4(6MB zu2V9!gAeAm8N8#pt~Kc~S9~ED7`{6kj#ezYp&mX(GB`o|!$okC;keO*a+^4vUi1RV za8mH(E@q6Uo$wj^(aAH{5e_E%_gb-mO{m)Uy^ue4V)B{GiGKopzG%HT-iK9-`1Cbv zaqxYTR+}&_XoKmeQL<{#tX1ufBC6;)wuek!5@*hw0VizP-V&-NEUZ_+k5B`e-&jCp zG;et&0|M1kFNvT-JFt1mtZygzg+}E;0u74dTen`~8UABdo3T7v$o1j>8DQfRuP)nk zsa(#+i3W(p=l!nqiBky-m9`o7T7Qi~$jK3)P$6xC=pY*P*-y34pZ2w1)nnMQ@hh}? zVwJ2YyBT_vuIJqARJ(sOjWGz{aEK)u!d~b1q0{Mj_*o@=gp)r_zh%`Gq@j&z=yva8;Iv+4w(H1`29{k|R=y$T((-XooOkqlxiZ;Kd?CAH0 z>2MkjC0w~bw~`D5SS*~RddQvnF5m878`er$D;FIdAgcWm#$?VMJZYIJwK{p$Re(Wu z0t70(pw4OJ(S!6k)r0QQP8$n^^+yV{aK2Ja|1~*c^&Cze3JwraFgZ>39am4T- z86MVceRjRj(hlW#o9ILDCX}_91p)gTFo|RBqE_t#^eXzwT4;YJ)jZ^_ULnz_SkWJ| zwVPDv;J*zpuE;YG_ON98eBiU4DLk|Cc7YB?Yl9u2T5%mhwPAVaKoDr7$ei6xqnH%G zYdQ$A>)+CypbmP{Y1hvDJP`=tFP$@?N#FE`{a5+-IGX9st}N1IzPJDXvH~AZi#+Va z=cn5{I}`2a@>-7ITWT7|AfH*6Q{gP`82IgkjgU<3B*$e@(f3@78gs)AaWkNZRAcH0 z1H9xafmX1l@V7J32N?H)5suZ44Fz7v%7Q)E<6M{#xQf?b2z#@#IllCyF8icsIMO2H@Y@&Xe1e!&0#Fg~n-@UVg!LLr` zZCc{$Ac{B6O!aaGV%g$@O9{qWXPuG9v~7d3df*wOx1WetSZ-{vab$f7{(#g^g)n<4 zqVNc2Pni*_Ph0JP@pua{2w0BR%6e7qw){3~0QGb7nR=@gCQF#Ucs?z!30ang zqf@!L--c!?FTz29<5$>EQ?K#Zx6{ia-fvI)S2A-$OIFF6EePc*Xtmvv@+KvJ#f6jd znY{F@=rz0wAmW4-%PFpxgk1|8?Ol5}c0O=GRi==u`11Oyvvh|lk_`JJ{%#DSM^*{) zyK|XCPG6yvB_o1)iU}G*_ieEhPN&ohU(TQey=f$_IRSec2K8Q-k<=gf8KN*V%N!m+ zruYq`EZT82I80&yvTu6=+lRjf>_rrYdH2>Db zYbdu6;XMNb^MwHvWWKz)8*~H|`DzUITpDnfyBxWZKLE;5`;LC!ovPy<5%e}_j!cLe z>;;z~SWv<`J5QD90dq77d1yV93)7bVNOj8?m)hk`&)v?3Jsq?2Di6n*$57Co}4@02gMwViy25gk@Rq z)YGSf?wqX5!DiX(62}I|>R~8bE*1nV20bA`eyG6;J1rkO*MU^|Z+W{PY|3FEQjaCX zOL3ie0rk9df+X^bnp%#p5>_)$hp(IR|NIuQ-``9Y4@%c8`P4@6RC>>NHQ^zV=upZ;FhHFGgO{ zCT1}ub@_bl^^UqJt00VX!x^vw=o`Z)p3-m4sM5zX+-aOWKxjx*ZVkE(PHB$6F;#b8 zc5uPeh}L>54;F$RA0-aVANtitj{SXTQNHloAGC`NaP0o52*EIt=lvs-$xEJz3JuL< zY*##e#H2f8vZ>EhDVcxBTmBP_Z5oX@#H-O~rjT_j?DJCart}T9$@1r?W^6C~Z%U{6 z%3k@K-;mz&hV(bp?@j5IVDI*#kM&!XYBT(y2bL-CBM&e`##{b=*`OvyBl6mXFl!w? z*=MZmEvjc59z6N22`BB-LC0aM)E9B#2_Wx=^T0=b{`u!R%=%qnHvr5vl5DopbigMf zx#pc`S7kslMqx1`3nXvi2}n7Qew18Jn1B&}lk5K)m;+mxdbPWjOsfrD4Do>##4_lP z8feQc*1bL>DkF$pd*2^@uLG~eqFj_u0OnnNgW#8Lne(QJKloeTd}Lk3CtY9>cs&k( zaZ!A8DIQZ)HB`j`dK2K^xtpdqZe85e{(3pj9Va<=Ypo}@82ub?3n?@8 zI2p6pKcDO#HN<^;0V2SMymS2xqA~my$+y^!4u{WjgvRRt|4}JU)C=~_e2~OJ(S!xY z3~N~b0TY|F9mem^>_YvG`$x zh<+)n(a8OxT_n|Ql(ovDs6&m#a>p2Klxq;X9(f9uWD|TVt!k`4mNqHxccP$G<9B9( z&|F4eTb==Q=$Yzv@yV4If)Dxl&Quidp6CnJup-5Xy2`+St+}sN*eRyat8h9mk{a;E zRvqqd6%h36YgPs|*bTY(oTEJ6ehUJGV~3BeEAV4>ZH5${3aS?Qc?s@;QHEq1=5^k! zUgH^~#Wzi%VH=&RSD<0GXy($TV2t}>il{2s=#hwF8T>*6cc)mVNmXkTTPUpu*vOX! zQ|E=Kv$D(XhEKxFmoGnsx;0f8Z=VV3F!iPDukZ8whRf7R@=CyFWZFzT!dw-BEsJmJ z#Ia+Jv9=LGd}GmK9_cr;1&e$0Z2h)E7)^j!!fU1TjCo#PTJO!HP9F|!w#}qt;=`J8 z$2`8j-+gO2vnD6(ev-7p>E%7*^eKC!x#-Ikba9wx{}|iB%+Nf9_G~tp=87;zju1S9 zlp!Z$AV5T3aTiQuYy=8JzkXd#HX&aXnxhP8X(}W1w&o#@yuebX)pJw0b3ov%9a?uZ zVO_bo3JKSt939ZUZ9;3a0!y?W;81?|4xn~1A6Z5F!`myL!@G{m>QymU@b{J4?Gk8f zN>Hv}JslhvN40z;!wkW%y#^kzK zhY+U9eg5TEv<`5x=AyESnIS(Zzl@-&!XA0PNjwJNRGuzPkoFYrL5}r2$n6?r9aB+Y ztHc6KQIrDId?)pLkEEzzGoN+i&Y|8YdMk4fVPen)S8bq9EaD(Qrum^x!-Z9mSF*WK{m7JnO zp;=iC#^jg2b*tgsn=OzKtl=7mX-ur7kHFN?;9FRzR)LE4KP}3bZz`k6CM*Su`l`dA zd<;ZC@v+H@AzA~lc`W8v)a)la(cyVyPeK&#ipu5J@e2w>{Q~1)5e@K$w_DdT_>N(*W!LBc<)KoZo_$<((!rO zh^A7J7=VU8Htz7vwb+u6K;_jTD`JP#baDUa{h@t*-uI;@IG%Xpj?|O6!@#QaeKHMz zXuduGW0^SVSq6ud#}rHPF>f?X)z_OLJgeoIo(RK=uXC1}G8~JV`rr(ds;}DY=!(08 z_URyu$4tNgRD>mQru5AV2q<+C4!d6k*cubzgXoGHYsnSgT;VW|mybOk1QEVymtUtA z@icknUL%Ump6xJ&qWLB=$icDhqftIc?*UXm`0Xw%hT*3q9 zxxN8gU}!n~wLQlJA)M&>5|NvxE!)Qg_9?`sFj4WPU!?k)9^anXP|Lcpx_XK)2j9%e zw#hZbNyQ{qSN5YG9|b{|cnsy@G6sZSVf7SJhDi?zD1?z)M|`VEaEH^GH@RBSOMW}- z_T;aI2@soTD!iApYJ6ArUAL+~7c5tIz1j)$H#QM1d7sNO4iUle7T8Cy1kAawr<$xInY1f@8_*4V-O7-%> z(WAqt)B?6j1J;#n2^v9KEkDQ!8vD8I?RcmOw2X(IA-ySO+-^z>5V#x7TGWoJtT>el zZ7}Q)f;!QLih0dtV{{+yW*_SWeT{NrOr+-4W7Cke+)QB4(Ef8Qi5+}YTOOuGwG{R9 zs)_tZ1-R4vHgTz>~=Rr-pIzkU{~l(}c!AyWW(tPRO|yO1o7w|jbr3X8WZ z@Et%EZ;9s;?h%kEI|8Ib5ty zAaB=f|-E@j7Bx?>cquo&J+~UDhxYRSGvMhymxYws*yK)ANH+8 z;M3aQpH|89$z&Z@fj8#|RC1k9R`=7eO0V_W`j^!}QS!NGtCA_|O%MB*S@lwW?(=9| zom`b2vLp+4sS`7~nOc=7(bhI0)XHLY6#OVHBbJES{@#UTWvICl1l$hTeU4P zRCx6aV!$mZqLuq!0us>ipVv5SItzIC1zpr_N6!b-3I_IV*b@GxGxS)y!$Un0CX|BZ zQrp8J5WUswzSOz#KcaF&=)lLtoYZM3#x;Qa<}84D3)hU2rfP&0FpHpZgnoLCr!hGb z2jxfHGaWcsC+q(Wuz9y66Bbt6g_#uo+R(~v-ZbQu4z*@*Rnk}5+O!Jp*k2h){C;tB zR+b~|kFGrNuHot7%~L6u=yp(vRte%!MwY`(+EAzW0XCAO`lGS}uhScm1n#1#=Dh3N zWx&M%z6cO+1UNUUsN6D6-bvxn-Th8{aeJ}ILA~XRzvO7ANeC94nfvD{6GLWuME!zXVD&AEOjhx9|9Y=3qKu=id-A0s`fN&9W^k03ji;)SyJ2++m}9=rS(&juxNW z^1l0fA7v!twa_PSUr=1%B-V~8cOaEr<#uOh_hOSc9MF(^m?c&I#4#1=YURFys zqOd)5LnC08`&{@Hnqv%{IFxHbQ^Z5AVZWQF=y%Yl#;;J3o?m$y!!EU49hyK5=>KnX zHn6fMPv0zFAhu0AKzJ_S4XYG{k9?t$m`s8Aq)=OO3+%+QcnU-L9Z?}lt)vhB_|ffX zw&04h4N0~yh{x%EBx{~x0M?5S@-0*@=uNPJ%_1-Aom^V0D!>frbCO5+QGMbvtHMdPFx)F;F2=jkk=&PEQ?1e_$?(zzL81>-tWjIM!%Y4D2o**vhBiay> zfxcyx1$6~s>XaC?Y!QuM&K)p-H|P>Ginwoj+)&6g8kU$;^{daq>y11BFAU|s)1}vV z7qR8Ved09+kj|Ys$;0AQ(eQZ`0o4X`;#S&VWzQ)keM6v(;UmLhz8%8Pi_sk0);pkRPE zzeVKZco*W?rUX0c3-5vBs}}ha&BXnM1UNrp?{un3_R3G+%U$|AdHxm@{weP|^>$$j z{%rn+@M^876?u#CpbCbx)9OROWeLC773OFi#0S7XuQ_t1sewphK z5d`OV6PY{Cl-yjSFk;+iv02oDyoeU(Lw=0*JIdQI!I<2gnpS03`fA35R<ffX{5=0li+bNaEKK8h-rIDrR%f$ci{Z03 zY<9%9@-ZURgGuuz9-wdy!(Ix2;u%Wsq#1g%Kj@fvez z#aFlV)l6&s9OGV>ju{K?%^8!RbmcqMl;H50*Fc37h(yte)7hf?XDWB1LA5{*XoaJ1 zQM<4UQql{dR-V(e%6RXOe}N@SwZWP1d`)rWcBgO|QeM1*`Bp5;HV7j6p_Wce z6eCQ<$4a!|attPIt+GPo3{oq_2<_M=;ZWxAOa+>P2{?!4rD;(Cj|MOuZ`Uk%b&H~s&vpOXss zfo$rN=LPJ7{LL4Tm;~6qBN_HP_-aJ&nAe4xy#={c&vCXdGbS~26@lyzbURI;)8$Ec zw{JeWDcSKE#8Tn`lU)2Q!(m<30nvxeY%c-Zp(auZ~sa6>{G?2K42Q>0LqG*s~3sME|ju?@asl z$BKWOTxBm@;e$-h8q9U0`DVP^qE)guh0i`tMCIga(ye+}Bkml#_~VrKDRCh!e~rPm zDHXpJm}WV$U>-4vQIQ`v(1l|eI(+u*1Nx%B%+uHc+f1(2FYrGv3gL5`y7S}}>?;Sw zS%)Y(Vbpo%CQ&ufi!S@8fm-OBdY0j5@-s$ou3u{3)d!65na!R&U7SV%&OxqV%F&{G zxJ|NMR^5Xe+0V}+uX|V`wHKpy+MnDH)Ht->OZYTW`@P0OK^Ka?msImT-F9^!h4oKGye}%uoOW)h!-@9R_PDEb8Ap2%*WqgVP5PeN8GqjE6A0Z?nubr`@16JC$#B*$ z+ib%$-RtcWpVo#mJa*6h$Gm=;KYa?e&a(kTu-`rF`z35p{lk=b{WRlL+cpn7iE8Yf z7t-3!{AwK6o@-i*x3OZd_kFV;29dVT_uH&%({8^g%gEb&Z58Fu@dwJ>Uw;AvVq1Wl z_nKulgxCyx@?KTKLyv({WO`tT$bjjhonXjtqPeYJyZ6hj{Q_pg! z2TB66;WKi~Ijj9dM{4TAx7>#*jS4OP+H&cKK)@Hv$iLy=JAX|h+{u)qTK4<=$RI=X z6~9oDO)yWxk|!aN#K!P~Lq!F+PV8_9psMMtB!HfRAJ7{&p9Msr4uZMlb88QZ^dSeW zo)3WlHRGUR=-npoLSd?vo!{)}{glmGw&8V`yl}l=@}g}z&*jJ&b=E=6x1kaVpv|?* zW#Uk58axO(VD(HGLhXpPyqib@`cdybzT&PC`}18{{J)r>eC>&*(KV*ZEpF+ z`~d^#_T|>dxZ#;R2t9Qv-!s!t@oGubZ1vUC{4kq42$4T4j?qy@Q%gc9KVgrd#Fj!NT3;m zYS^XYL@423g9C7bg2}(D+0P&-V|cGzReeOdHrRkLSEnl+B1%8_fR@|q@}XDsVm`uq z{{84mI#2y>t_WV+q80kPMsXwuc5+MTs2F-#;AG}7qo2iVeGS7Zby-jqweGO8&`4g% zU(N)<(Ftr#@Ej~u^@3qf$W0%Gp0@5REj1^Nomrs$z2NaaGjWp*Be4`_343aPYX0@%c3DQ zYYYm?Tn&~sOh~ih+j|yyFF-pl3Se8DR*avUgOes;fBZ>`l=${ne(Qd((@1b=L@ehH ztc!E+lbih>sI0hF{^qk|Qd@HdR&6-b$Z_%u$P{b@ox?BdRxc`Pn` z`dr+YhZa4JKUJZ)`UegkI%HIS)ADet?P3rd*=-7|^Dq}Twf(m)%s#+gBYG?jX?I}) z*`Yg3S;Jy8nih7fS;{~JUo1ut@f+*rP-$tY_39lyeyAGxw;wCj`N_bcTklNV?0VDd6sf`myurm&yGtGI?+b@MYI? zey4Qr41==^$iD4x#*Rv;M(MnMPfASR9bZ#pvE0|WUaRa24qu4~3;kH30`uj+(yB`T zHC<-veobv`G0al$(zmoua_TZ8Af0Y4qzM9Y;Qa2-eT_S#%J(q3#B+)6Jagh~g7ahC z?FU)upR0G7SNoyHIs~oUQ@8mYvAqZLtg`ZM7og&`T6EYkSma;nS!_P368(^v4Z$ zZ8%z+Hn8%|iE3QEK+90T)oGilK{r&gfV^Nc}Ff8XRvKDrS zW1=)JItZwuer7b-`j8V95H|%fA-hfUlXZ7Q0zMqE3s)ymf}K6aRJsWhZLe*sGCo?g zzd)S10=t>@$gMqo8Q+|5Mad93OR+sh;S>Mv<7H129t_iShg;Q6vQ^dw^ZTEuH$!^y#hzHQ4q_s zH^0l7q67+NCFE(BOY(GKzS20iOAV=IU;i%sK~wq6t3PRC1QSL=rCf zeK&rb4U0)?3qd>{9jXN`r@d~y;H1Z?Zd1)PT5g)IO))*@U+}>1dfaR|M7`C5L_WdX zQ!VNxW3Y|MvNQ^HU(~O%n{P8G&*eZ+gdoKfp>0chJL>>{pRYc?VcDJSaKh)U9RzpS zlDB;=fV?|(H~ zuwwu)yDxK~eeZqAs=eF1MR#G6#8U5_;ut$vofGKB&t~)28*$QTYwga8w=J+5@YdR! z>zyWiY<~0lw&+)z+CJ$q`)ffPnrk@k2#(fh1lLH2eoSxtZFYUl9wh9Flb=Fv_A2l0 z4J@b69SVXuAZYM*wO^><^Xo5|c`D7iWxmvq3U%Ohv_8Wimeb*#OrH4RG$ScZ`K5g{ zI>i|Bb+sN<_Dtk!d$|e*{+1Q`i9zHClg*|(S|J9|NM&*9>ZWuXBrwu;DHNq-N^=ka zcTkL_T>=zX$5*k!eVIiPd@@ZO{6xP{*}CRZdTgnJ#$`GD5x6H_cN%pvP*HNdbQ`)P zK%3Z?U$BT#k1GhDLp&Y~t}O5C-98tGwy4kRpNX3khp^=vJ{`h@#_>(JTGK=}BbIDt zo0`jnzOwJ*C#?#`O6M5~J8GpuU|e5Vg2Th;d@RCN6__Hv;CexT$DSf4)rvw0=R5>F z<0wKC>Vdekh}_fTyKt2;a)b1J^W!b5I0#W`LeoTDf7`fWPFg z^_6~`+|#~*>C2woNf0os93gxS89bT+k#+Lx1nK*```GK?AFSD-Cee((v8S~!AK_#dB_H6S%jn0DuAAcpLn2d^dgy8 z2NdorZcM@XAqZ6}Xk9b|NJm)_edm|3Y&tgcI+-UWt@x(kAb5$w_Xya+W(OJDQRF#etv zacQA>F-0r9mY@eQyI9&#rGa{5>QkN(7SKVmahu^ZO5DkixNw?zJK_E>bxk|GDtD)C`Ha?c^({1Hr zMRqqWn&|?}T&R?ihbq9oYNZG{`-*fOAPOh?IWW;XawCS~yJ`zEI4}U5_SJ={&{m~f z?h#WNzz%m@r$-Ce<4nlAFXeA1vfc81Jnd(fTvvT*5;Gt>#G&JdkMr-S1=I)e^%^E} zj$rB{&6nv3Ker%*X95R!qrWrw7X_TuVJ$kN(Or9q=#tH%>_=K^mDzaAa}A1|vb7Q> zF({($(?7R=O+xc4ugPAgI@xdL(f;j5@}Sp~qzQgs=(!$_I5^DX+v1U9qo&K?cIZ6` zI-pi{)Y~XJ!l2(qiQ#Tm{i|siwKIMXCrXN!{fjLfpz``EQ^5`d9^lA#ED{Rj4i&oV z9+N?Xn zG9pN1rqE2;efZ2UX!HEdfvt=bTX~41FC%~5`^pTH7PmL&?Kg|mr? z-|-ARw90B15JamLbD{^1^VK#mSLkoP_x}bycrXrQYL29#a6HaAj;B>Qh`!7#H#L1WDD;KR(U9Vt zmVM*7UQ_|YYo8H%aW7IQjooNWRCH59m1}h3yq$*UX;t*9&xWNHUI`3T^Rpu>UvUGD zh_TjSje$D_rIpQ8=sEw&RrVXsv;h9=i%8$_<^E9#IhAVfn`YUYYrW6NJm_$?t%wgq zPGvjALF+%oU%Ujlox)y7KJQjw8n_t}oYM}P+0xwzq5a7x?)qMOEH68< z{h?R(()fpvyn{?P2CVa8(>M#tr8ACki2@phlzPt5b1v4Z8TMo&Sb7ba#u;!up*mbg%613>25SO``p1K?S(1TMB9VzY=!J9rloZ zV1PKq{yRbwRB}NJ;**>6;dGemD_;)+!WdyPvL)=k^LUz2!FuQk8pO?B@d7X%4kgc^ zXi?I{;wr3QU5?h&)KTI7#OsniZy2IQL1Ugijrky@%R~BraJwb&Oh*lffP93# z0!KHOF{RDmI6@&)i?8Sm=zD$m=$12Yu3cH4CC6^~qRQBciPSJx38sS#9uzs~OyM)- zA>f2#LN_(3HBsr(5)47`DJ3#Hs_R^6$8Cen3SVhNGEK_4vmZo(a1^w@=k=$^NGnEy ze8$nY9R8RtBnT=&9<}vKEWv}au}`(I1e!k^!Io`CJUDr-86}yctW(G+%+QyH=RNOs z5pTLv*!{Fx5uA+F{9RXyjDu&V#I%?a<*E#ZlB!o_nUcn14kIE+do(u5gN0YFTxk~z z@Flbv{%VDx#W#LF<903KTv8QPxA)oJ<= z`<@S}b&j+Zl&s~|PpfzvO%U7o=>2w2{FTG*lKx0#+gL%Z??r32&agzkh3s*{j>ATP z*x`;s9W1UEof(i?fJ`M`BN{Owp6Epm+IBMRPC_C|uj{tUfgE6~Rw#bh2NG!0?G~7u z8XneBR&cyok|m_wEEmWT5a$95;JW`pY6QZNa$x@$%9 za;m^=axL_6d!_5;(VM?$1K1brHQp5QNgu{94svt$eE@yS)S2*{fwz8DvfuKo3Pn%2 zTz(arpQ+>VmaL4R`~ug@1NeGgH|)bNP;#rc#bJH5hdo`vQ)H$L`zNcm(PNcrGK$DO z2I88T7?9W8`5kZf!`ATJ&p+=*hmPORlX$xt+ig9V^#6`<$G=WT5IvYo$3(MIN!zHp zv2h`WavZY=z0Yx2eP{oEVF+RBe={cB3Cyk<*EL~I^rt}{Z6fnz_{+UkkwXoLPZ|_< zs>^wJ!gAh@06~JqOa!E>$|D19HWR3*qTz9oFK}|<^<#Co?+ME^5y}5od$6vl_GBp1 zy$;9>`jM>NOOou#-EVY?w37?XKDAI2)0&w@9*Z5>6bCk&$98}l*JBAz9FNI^+jX4C zAUMz<7x9SDof$OMSk}3ltdZYcxiT*%Q01syvf{hgB{NEmpw^)!wS+6TObk>FIfOmP z5Bbme>3#KQv)3u|KgjL7i-}QkZ&wb7wVr9|h2SZa6E0;w>UpvSIt$FEo>6gLAFjQS zR}1Ji0v7>cLt1&BX*=qH_t?{a!J^z(xJ;T`gg$DnK&nTInyDqVIZGSB;#A6luw zwAm!Tz&WmHi0rmGfjIt5_G47@Owlb=CtKzOT7Y`T-#CA)LT~yAHuBuvcBBQV8rfm8 z`8W>DoOTQDc`GeXZGvIj5XU{Pq1aKu0)o0xo6ij;C-ZhdXH}T0{nZ)lHiqJ$8E49A zL!50YG6x7(0=uLKO`=IWc%W%((W;pkmkPc?dbBm0{@svIguQfsy933}bNTIa{`dLk zX=-9*`}to|(K1g$OdJ3ig0O+p{{<}=to#mrb$-rinHfnXng6CSSFr{om`&aG$Gboe^PZm=!P||4IR~(@ z7+0;KWq}5N^viZ2BDScN6g?iJ@U0n5=FIQT^M~Jld`GA~NLSLwq6xRV#G@f4Kj}d? z?QyhZ;fj1s@nuO1n%f>8EVCZp`6YY8FEprmkSAjtuenE{&dg<&LOVz0$U$4uv5W;(;j*_DAHSWhBL$UzV@9+v2QssB+#Gb@K+sL;iWugg+t>K5LW6OyOR zjtla}>{nn0avl6sFA{d6f8^ULL16}Uo|PaGzl>qbHF`#8FKjjV#ygp(b?wsK%4-BBWse{Y6OS~CDwY_NO! zv}gugTW83GHUQ=HgL*e!5xLz^*`E`}wTLkUYt(fafj{BH6`d-6C=-HssH8tS!7bA+sIr{a$K@COn17BMdj`C5cgkUg zCzWi$Z1<&X7C$F7qZ{#h+$M*wU(bs1PhCHpz<$WP1M(x<=G^A#CbP=GYFuPGLevDe zv{H)Qkk=HfD}F1pn&(zelxm(3bG)FII3wE#>vR zHt=Cn!|b`TYGJ~dKC|e=D`h*zHz`7CDxj}e3#Ro3Sw}&tPW07g7XHJ=uk5LLgTWqy ztt5*26Pm(C7a00}-1T&zniLrA(#*9MgW-r@u7!!l&s53pT@E8IpgV^+?F_2fv)bwAM(7vAp-i7VO zm{5|pG{}d$|Cre=|9P&m{^ds$}-PfX}p+9h>8POY$REagIE|skz zEwoD2o2O;qvxL87-lZOxY)g^fI(bL9gwRR$R@J1jxGqVE?sWQ%-21mn{{AUE&yV{u zzy14yzud@7)$Po@U`*=Nq?<}SxDM-SEuG)Yj7$Ub!z{i~uPlQF7*C4<%bfjt&lwjbU(9i7^4UBupUX);y%)D9)j~#|s>4;biHmm2P8EdqE|*<2!DcJ76BL z;)xi1re98meWLJ6B^J3{OJP}>hgy~2p%s)k{F4vWU>yF);uz%W*SOs1=Cd|@3dOe` zu7)ZzUg=}FemNDEi-Ll?l-4QEIZiS+x9ZS9`XbNl99U;N1+RynnM@z+_sCy)*yS4S z+A4JaJsxEZT#WTs*?5L|52@s-u++e3e?M)G@Z)sn-qB%LFyH8yXccrIIZjQMUNp)8P*_@~Xfy?s!c) z&hrL|l?&3~VMEWtj8W6N7vv&NtA~PG z(wM3xV_^_Pr4#2GWKzdpMNQXg9X*_a8v`7@ob~n0YT*)-pns_eq4)Nh9eASP*B226 z0%y7{5-Z?W6m`4>gAW(K;85;lSxS`B;A8_hS;2 zfhSPYG(rFAOfZT_%2$5+#Gb6f1}dfA*AiO9m5wv+TIEBcrCg-9Ag)z%9G1wDkuE>@ zj&liZByL9(X_;)g^zZsrWmi*W1p&=hQb0R`CvK=Vi3tKiO?Xt17via6;t7=sdjPP< zYW#PgRgQf@;}u?{X(){g;nHZQ0j;YXhs2nlNSC-Kl^tkIKW?tlxXGmRcSEjZacSst zor0zj&tjxNALVcV3gO)(UWW74JK!#6OVXrxWJ>)7nd^;wZ>6nBqJAzVh z_UEVKRuJ48R@PrcJ>!~BNXPlt5uP)A(tn3v!#Zln2>revioQ@^dfUCJc$6(@j_V-d zYXLi6k2}3QvcR-N_+^A3scTZtzHlj{&hx|0$nOk+>i2L_(SWFwl@OZ|@eb}5MNy{F zGVt&F;ov@4TX$9LY)?(q*`e71orcs$i>BXX?)7n}nOCU?oHUD0Uhc)Nk-sTDg)n6#${T?4iR~`V? zv>dn4-mMdZz2_VGVaC=x7!jZ3G6+^7Z`c86>HIrcjX7vCRz_MdzcqkC>Gc@XA9yBL zV1F>33n`(Iob3P?)DP7_2Imp;bcXKJL$s|;{WXh*xJP#*`^$_O_@M_cAdu;f5xx>5 zAbvaE#1xlp!ur1xvw3w|;(V(bCCww4!{WtiqQUQN3Nq;o#WRop zXRwV_iiZqP$<-dvrLS>EdT^h@+AS<|t*{lQp{qmPPzjJxy%E^KryDm8tVv0WYMFYW zs(O%Su*sIz04XHV#6?bVB&Qsq@U}IE*o7$8 zP}0Egw=ef?Vdj?x(D#nr2;*4fubFt1MMNT^S02h%!UxTeW@N#P>5_%;OG>HCH6*|L z?s~s^?1;y|D%a#D0fe4lt^iIWbD>sp2 z4ML48hOpG{txb5qwLpd4C(2T*YS%N=I#pKmBs1KUp9>+O^v&br*Y&l4w{TcOh zPwavf5&M&YgA^4@Egik}8=K!fI06UBI*1;haL^sqN{1S6(47)mw_>Z59-*1`=~SJz zCOnvVn>-9eL%|_SQb0O=CKngWV9*AAX6Z9;lizwppIys1{d6zVN755VoWz?YMV(ik zR!lw6szb!3k<6&v>*;wUjs5n5qRk^kgcG64KICir*J4@aclhC|QV!0jrmqY=<(KLX z;El0)kOJo|o~22y4NaOt$q7u>4M6F3yvrr zw7i3A(neJyOEej}nUd^DHMiiAWj6=?Lk$k|>vFof-6?M4pQ-L_uElh*+LJBiS7(1T zLyc7_17^sv)LkYyxTZB6PA@tF>C=}GqRcZ$0{Y-N8r2jqsy@`N$3rd%PMLPK+dD#l zd6{3@_9QgugJaD|&AAcA$^Yoh(S5!`9_PW}jN$H7L}e!D#88ZK-SJ>a&t(gsGW$c#!>oVWTr(E22wbnun%G&9cR0T-U6aKwoO0B^gb` zIylPXY=`=p=r^JhWFJF*%OHzc;uXlI9w&9!$CDMjoS`QuT*5>&M{C$FF$iq}kw!UE zfYkuvfUgqE*xmBJGrX5mMIT#GU`%G6cEwjI6#BOC2fexz;tA}vasa{2u?Pw64gur< z2^tj03Ve(fR3k%MlKbP1lzjRp0gory1_P1<2`1Eg$Tl>HV;7`AR1^+fri+{nv^_TL z;`+Rrn^tTOT~+P|`XI^RwG#WHXG!W1dc*SAA44FU;mcwo$T;mw*^z<4>F<>AO_J=ID%I4UTlvWYDNi>dT#h{PPcgY2*7n ztAG4#@!7Lyb1ToP(0&{15YIB|Dj7;wWCtx|O#bH{m~!N7naArDg0`?8CI^dx|9I7k z*qlefpYF#p1d*l4SpjHtG>{^|q$thekDe&uPOc64TONu|*m}+2S@TP{Z*|#PjEg#~ zh2MW)@EA+oYEF_H3U>QDo@rMdIqD+Y_CIV(BbQx?w z{O5MXO_6FI?`>DST{Vej@^#}(xvctw(r&RU*0n2+?DjQqT(5n4bLiu;#yAofJ**yD za8LWxM1507P9%?1Ll^}`kzL^!X;)@^W#Fk`)Gbm(S ze5wLoR@J|sLlM2;X`kD%e_RYzeC7b|PYkHt{d&quFoGCCHtc9he>Oa~BUBt=Heqzu zjso;N(%|w0BAB@dw(exQ(@?wED7bic2=EP}E~s*TjBAs+Intwwo0MOsUVenz{b0DT zSG8Zs2ae<$1G14ljT%0u&jzi^7I-`nT~4+#oSH$WE#auRBMidxIYxnY8b$P+1Ir3# z+5xkwQUG&HuVSr^A)sNF#XTAV@^S$^SBP}F{$JJ6&>Q_~vJd@SLu+FTqzgq z6t;^~qCrk#p(CIZjRMy8fPXywE24KDvl5YVUDN4m5e0Gf4e13m6ogr*quEvd0_u2a z78;dj{~TZ1J3ZwanngkuNGj z^_@8^GNXuKmicSMOI~BD`#hQk#P=BhxV3SzkCclPIcn3{G|)Cm8dc0=P^8p0xz)R{ z59RczGG3^)PFpRcktl0H*7ABc=-Ji55i$Uz0{}meQONGMH5kK{^so_HakH355Zq;K zmIfIV!h&%=&T`5FiHe7+gcHR;_)F2**Hjnq44)Ml%f_ecoqM{b*h_`xfG( zr|X!yA5gTE%Z!s_D*fHwzC#`s&cf)N9X2s5>s zA0N#SkoJqCcNnqkVm&CXaP$QTe8V=7%D`nqn3@`Ge>7F@{XW?zD`C{qB2H!r`HMWg zJFEFJ#**^Y`bZ^aMQ$^kf5W1U4kPZrD=^9S;tfV*l~+3-{I>2~Ms+zO_;T(N@%AMq zKCgU@o4_W2sW@8FZ&45gjoRMe-V*$m=JLBSX!TU~yZzx5?Acah!?-l9HQ4e5hj(FA z^I1?OUG@X3Vm3TWb;UQiEBhEBq@CvUfMc;?{WEn=3c! zMVDd{rm^(c<~KalhlfIv%Ui}2H>>XQE7%nsPOcZx-j>8Qicsiqn4@7BV`dJDIOrM4 zxkRUkWWWj-t?e{c*T>H#b?G#JX$8zFmvDj_c49wT%}%m5Sn++o(kwEAOiMcxIiiym&NnPvQrE+Ia+D_rQz>_Ly5N)};ozoUCx2XFh zb<-Ag2;`_f3=9#`%F&mw#kds;_fy#+^~WX{O|SawDf}yjh13_R;ngY0KWG20uc)?$ zMFu*^^^X;?Pto)-Wig*up<7e-xGgSYhmik+*E=L=9EQ9$<;6Tm&?vga$;&(@n%9VD z3|yQ9z#Ley4)lT*XCFO8r2$SpK?h+3$4-Q^*-##pSj)yzT=ys&Fe zjr7?wp_s=P-&8X^YK>x(=BQ600 zy2vxZ&d05{1Xkm<|9g;;yjTto8=5ogMu=CVIH3BwMd2O@epWZP(Sd1MEnmrM4Chnw zeyDn;m4wPLADeJQ9q4COiZe-pDAdd{m-Se|D8{tV3tL*v&3MuUeh?fXTqUZ+S10Nv zVHWd5D<`xd*4?RVz9{$ej*7N8m(7g4Gm3iE77$H^{@0Y&`#-oTOEE*T@Ql92|oiHHw`Plnsq5N05mAn!4FeWsmhZhiw-!0;@W(zgFDr#Yr zXTGihwlPM4^)FLlD=_A{n3gR%R2KxDg5P9s-mJmA>|))P&qGRO3;QPy{zLrlQCdq? z23nae-VAzeBUOjN-`zO~EH4G4@UnC>XTm{T@jFN(itZx(x=kJpbRGlXa!Nbi4WJ%h1e3u5{E`9W)&)>9zjn?@zD_;fU2@Zalb`O zVjpbQH$TBqsr(uP=p>occJyrUlY)frqeGpTufvlqu(ubcAcn#{h#-nRH}PPkGa&+^ z=+|waug;4Nr};Yy?C;BT{K`WXTDb}#2Nw{m~JkzrN0CQd&$T?0tJXDVkNXE72E)@cUI5PVE38- zJL94I$p})u$N&wC2AH2eLtd9&z>;l65^Ts;erja8eEpC<@ah4OzTP;;)Z9!X3;^$M zKwXV+KiKgHui-^oLrutGaq83&yORGM;g8yB_i&MU9*}*-8b60(!|xc5r5ENpyjH~x z^(!{e6WC-4zoj_HSPOSY7O3_Y0746s4!-l=#dL{*;r^^H2?WEzZo zcyAS~2v`o7E(CtX(`w_>bWZfS4{FYhtqHQ}Z!mrnHV6;1ad<<^G|tyk;lVd5re%7s zZvH0MSqB{5c-1K|4=TvHK5GkHkJr2!F zS#fEe%1IgPkX4`9gVl1UFc+~7z zXcLqE;^Y2`Vt>q`3R4j1Ih35BdSnF4;3;fCR54Fs+v1MPKVhzrKYuf0ASR`EIKc_> zSkJ04-8E|phrj@PU7h@Rlf2Om5VdT^khA3)##~%ON9lq{sWmPp^4%7WK<$#&zyrCa z@KPI;M=qCrp6BI_#OIlup!~MVEe(8&=))w?(MdyJ&H^QhXw1KyJ4GUzF4yyB&}c<$ zfX6mjIhD?bdG%$}vz1(c%XD4SoHze|MGuZ~J-X@CQ^^2E9lxan;QrXFy}F}$Ug%Q} z(jE6xS&O(P#pjUMvHw=wK7e|lVYc2Cz&jNZ`tEBt&px2D!3Pf>TzxR57%)c*XjSNM zB#X!4hJSTvmvna-j=g0z?Kk;NSHq|0#)gl4eMl!)X>r;@8%fH&ItiaaI}JMNwP6&J zU>)8HESZl!cz|j1p|0frgv&%<3M6VYTqN)y)UjqAH6+*|-P$evt!ec=GR_&IuOC*4 zIne&xAPuKCSDM!%03Szwshz(IxVP!%JD*FFm@=y~Vx zdO{9itM!>dDM1Jw8g@VkHix+os{rR0>o2DZHR6WW0uyrHcc4gf30_SY5D#xbK%^fR zybfr5*{>BEWV3j5WgeOr4O!!s^jLlTp7O3GtR6HJ|1W#HFwdl*DG)>YL@BE@`=Jo! zsGeR8Fd1|=!CK&-IJmdn73@~{TPtW$-U=>(zto7Y72YiXnCG0Qo8506CoPU}V%-du zs=Wad6TqD9moG(mVUW%wD#9Q;kX!7)3IfdbD{$lqO=;lXqWGQ+=_}-wzV$-kQEkLv^w?ujYO#mra=A=tR9dNLx4fQLZ@Gv4&j>^Sn5Zv{e+t zx4_CSmc!9Tc(Y?nWrs|TH%;kILKl~EcD)i}|EqWzpMX1o3j-dj@5Hvq)6Vl>c=+ss zo`XfFU&7k8TaL++4E={LL0=j3I27$IduKKx!OyEo=*L*aNCcuUL&OfJND=S0KY~QXQxa~k3OM~Rwwbd&PE1>TqqMA9hSXaGm=ze#a;2)B z*cV2o_MD3{d7{pCE>EW(MJ4>;{Z@lW5~@J)3MAuUjkyc>N1i^f;CBC=cJwrO-IXnp zn&-@~O3g29{Q9!Hi3UD00*U4ZhVTu!Beq2)zAucovO1DEWU~p@sv{Y(+lZ|6ooVe}PUhV1`P?h5L)m=KfrJA%Vw zvjjqSzoE<;^c?yDlFeD@2xqM9P%!RIoH)yV}FE$+O34v*7^Ujk}|E2 zh^Z?LT@8gXSxXlM>2Em(ZN#CX*7kkFbBu{E&SUY(tD@kSVpLXR$!{)wE(At6$K;mO(45S_ zma4ml4?j zPGu0*ob4G|Fu_WCoL8J=Ez|buA*MZSbRZNahlEkCOKUAGJQs~A_@pE^qqHgRWco&au75#D!;U&ii zR3~mm?idP26mLl*>+E=Za|T;gp3sFGk6nEemrEx%|EWb_F=(707Ia==@1&LXa(Kv( z59ff#FjFxGx4!ec&}-%b#JHKSLXa~(pkWT*<%qFKcM;reNA`8PmQZ^gnYp{@1punG z)eRhmsCscw)FOkW_`~h5S4^uviY|ed?8m;cQv4RxhaEECF+p_(?n!W&3e)#TNwf+4 z0FPrytHxaWuDAg&73c`ArtsLQh@oNU52Y_ne#|jT92xid>_FQXF_pK;SeJRriiA4Jz zT8V4f6Kx>_!t6lgHv&V2HSjgeYq;>E%G&yON}F{s)_ki6R&*RZRxM#S8^iR<;w;2R zee=1e(cdY|p*}H#y%5Gw(uFP)jj;39bu|z&f_%t+WiGbkWG*#`F0{iKls0<2>I&1c zUWsqJymnA2rehew^|CMUWSjJWK8! zg5YEq^wCD(7SV&~rCfR7CMFRQH13XKaXwMB8SmiF**kuXGBlycZ##THS!ugMi{i_ zp3cPdRn09hTk)9^@{S%s*{_B$*v=(Yu6yHV@_glm9Ktm#tfBO^m=<^SV3t(f9t?aT z*Z)uB_#oT|FR{pPj4QNJt^|ypUwF^6iLMy;1x-yCt;wWUZTZPl1 z5*(!RZ&gM|x5Asq$VzU@62{MjI>4}4K<*HT+-3U>R%rCY}%&?s9&N4Srz-&)5e!Df9D=6XcN=M-Rf(84ci5{yvg@Z6o-G=0a~WPWdGB zTf&u8VC#JhStYm;ygAT{V}P&eZMD8|*2ip~&(-2XH05V-mgzmIg-OeMub>~$!Q2u= z+DCw$#0hJUqs}Y)L2JY9H%|C>qV);&P6|GhOk?PO&XCq_GGzZs>QQE>Lkb;OPJYc2 zZla282!x^f(JNZLKx@WM0GfAn`!%6fdEB!#g^ATH?6TH2=#Sdyy@clNoWI-?FPS?e zSeqEJI-r%Jx`snfuyK6NHlbr_8qkTAO?%)AtERz^KgFeqBk1)X?eja&N&T z!ZEU8h|ag6xjQBgU{e85Z8U|0f}LW*oqVbl5;J`Dsi%3*?sO7U_(m9BPi zqKF`K%A9~BKOtD4EDSVlszmh9LzqTS=oOV3o6!MTgU-QzR9$E_wU8qGk2cWJIkAu@ z(zqm4CVFMB)auDF1VB$!f{t3-?N*Xr1^Q6)W1LNczXzlvUt|0Z_3}1fCrl?P_1VIi zoEYtWQ4_^`IVf{YukF}B8~*fW1N!VCefCmWu+;Pn;#^)6Qe|=g6nI$xEI|bcuL%d? zgeFD9gdlW9{@r*@b-37EPsXJRl!W;sKCT)~P3C`6#9+Nb#TO^fE1-!F`7LK=Nqhmc zWO65f`{Cjz*1ZFr6f_9#<{n0wzO_-C@~5#lOw7G3vTf)LiH2Tgycq-a<{D^#ray5{xyt6 zuo|SGA~0YcJA0OLssojp67mlnsQoB-pt6BqH8dFdy-(kfmiMR}20hk9^juoloc z>IloYA%(=C*NRPe_@D`OyFf3aeT*tV7ra5HO{Oa*-~L*rNLc$gT`j3k7u|Z{1*C|k z`09QRC$*^3q>%J&wncw|?(cR*Qa5j~C(UX`7Tj02@Jng7Vm@~8)B2|U*wniC4xZ|e zGnBu}<8A~Tew;eBA(r6EC!JiHywez-%}Q!icu`T4Ufqtt`Ti*^iql99It07*4N-c~ z%61=D*5<;FGz{BA;wN7P*@p3)S2LEsw}KY8LkglMcjTbxvafc&hpUpa*Gw6dX+?UzsmytHfp zU`7pzTGq_!NEN;m4MwU&w;D^g0=_#g5M;IR2xsCzp+I*fnWm>e7SuQIP!ri)=Hn@l z<-&E4%4G@svKH=mWY?(o70f9**a#3%=Hq#%(8oi1Ga3sfD&Lfm8=8_WP5Qxie8aa! za2sh5_xyzD2WtLnOm)1%f*rOuUaMxpO=F+_R2RxE6Ct{csfSfdDHI__%nX(b(Psp` zYDb-}=@f_F0cMt%)RbcM@@OqnZ}nWc@8?xOtY^FhsqEL_F#PV_DWRQ?!mp?y&g6H{ z?9gL2nxk0(Ly}=Q53hAG;qIt`guiAVVs; zZaJx}Dh|_8V;~1QuY7^s{TF$FeS6_Bak;-T!M@p3m{i^dw?o2ZBxo12@G7?gzgC+- z6c9iwq_@6{233Y`7U|Ogk{SLO0Q!-1sO4OmA`QV}Z#Tj0Cy(~zJSkx~NJb1=+wkp} zKO6o?7CnD9+m8T$xf@Lc`X+a;VuJV{XP=?Mnk<7etA;>Rzs)nN7P0pi(-Du_v$i9} zNS;h0>8AEXd+~jy!q2fUY|EcDbSqj*VtEiF;O4)TL_)-gp~ZDj`Vy;=rodak4~vLM~pg12%>i;A9y%{ zv5E4@4$qDeAmuW8T8n*H6LJ<@-V`?XuXtN{qKku&5AhHu8t~W4vxoT+sxy^;rU}Ne zE%GU(P-l43*w>o6?Ekl@)J2z88>kZV*?;U8cKGZ-Am;?&r}fb26-N(>dG|Xd$ryO@ zqL8!rcffxC;osZ$j$Nyk6Y>o#Jj>*nKtUVDTQp^}7~g`ci46N*)B(vAS70oxLj(Yf2SEAe3-YmrD3EMAllj626{xBl?|Hic%tgO>R^HW;KrohrL` zR?|A7t>%W|B1h41twx$M*c0UoMNdzmL;Dg(dUAs34+K75XT3XuGP}dLmH*uV6q!xMdS?e0 zRGBw(S#kPlCIWAWPf$FY#}|QSH;3<0+auXwL){u2lW~7xWr5z{p` z6saX|G=;2qTQ=v%Vs`wHU!rIn-=!)`fgzpIznKUB{&iQtHtPI?7_1(jGfUU6 z3t+g)%_teca9~B|TnGo>M|jv9YQ@}w z)WPO?F0jX6CAc*#C}y85l)%s{YsY#0ot#pmBDO=x=*T93VJqaeiAwmcz)5kqmf^zk z=)NOeim_QJbh>ag3I+!n(=**k?Qe0@S7X#;*}7TiQ41gFaa59VNaM9s)s*qkA|mMc zDuNXOZQfV0Z|szbHTMNY2M);wft+!Af|k{g+3VSZ{};pE|4>f?GZpSmhkMZ*^o89& zM)6@NQFqRcn{Wh59*0w+8{Q-x`Ul!DuJoU z*|sz%>g2YpgJ}JPO~j`N?`gLNDDXtwlm^g5eh5?mc0_C>^$BNZ@@@juD|l)B^NBuq zw!x+nYXllV~LAgs4EsnczS%Rk}*HmM&C}BZs4vLN9)C8j0KKE^~6=TKP~^q*-=8 zW+oKHXriyl%6NPtRKLtr=`1E^UV7He9I&nmdx2XkcyV87qx>er9yW+!Y4*7g%5@n; z9M{SdGPI-Dt?VkLME6(jQ|lF%Xq^YvDE(}T6y14A%Z!9RxEA#tP(?x^q6zMlx2}CT zq%bwQ)d*}qv_Q(I;ij>q*9FAz?Ek)RmNOWZG~@^9hr_C=4zZ%HT$!|w_pd*HtWI3C zQy5EaNIiOD&p&y^*-%wKk#W2cV;NDMV?MCeSOM(Ueow+N$+XQ6q3PA6lty zKjii;Ve&2ZJxt|MR8)fyuUSCz8pMiV3*X1krPtq=aoSQH-gAy?)T$(H_+r316ny|Q zA1D?~xlqOIYsmM@Ufh$f^d(sf%lv9Y8FtjdjF^PY=^DGhgVOkH?`V6E&uxmh1iuJP z;LxdlrVtG&wXX?7V)WoWtR!`RGqs}c-O~T2XUfG}34xuu^!@t;&%Y$FcUPyoR^XXx z@QyDaPulS~&^Xm^FI6+pjUgzMnufGJZq)J()DJHM`{(PG5QPV)a_b*WSu;WdiD?I5>-q(6xmr-?(!EC8-q{ z`ip;C25-S);nZQ1C{D`*EomN9fr~`Ls8%kE(Ew!BhIKj>HroZ9o++PQji_GE+N|Dx zEhofx7lHpwFJN=c1EcSmb5Qq8+RD*`djqu}(@jRWBIRu2x5-V{Z}R8`DzI$XE%~%! zG4j#Rs?jC>J|1p7GCJ3$GYg*k=HrQZbF=s~5!VNVzKjIH&gNc?crAxib7Yg8fDLym zbe|V&7@UbYt?F3!Mf-n~HNh~+yhw2o?j1PnE=)B#&19_iP{-ol5T2AVQ^SQtTL1zIuGOL*H+>lW{MKDc{>Rh=V;i7Bm>1@lsy0^`Slkco9Y0qM%&gBxH+xkP z1I+fVC+9))AViA9sU+BO8SGn|(DAEq$D%)q9m{YV&e7u4lYS!~UqR9u0D9~Nh3^_2IEK#vVnEXHr7`uy0aUq-Lo<31~~=3Zb5d5 zdZ-Ye)r(UpXt|O;u0xiKwV5av7tf?|CN!jALI+wa2zX_H5mDj3I=;xHN$>06EX`F} zxR*wxp@(nLwBM?010$U`&ZTe>U0dhd(?uuW?J}3b&0xmenT7hRi6G{7ahpI#=<3?j z^{T`>kX$UvUeN%fYBKx%q2rB4!E&1_Silw;4651RV;uU1s#>)73wluU$YJlzr+M7t z^530!c=rllx)Xbn;I8(zz|hqOJAy%W6F}}*lT?BS@AWeTKG(?>}GQ`uKY)bewf=(6M!j1?| zGzvOS*`ge$ql4m|;6RkTPx1L5(gDCI^CUZGW!I(TJXRS6A@hQ%eP3~)pWdQj#21o zpUCR&*?3FFNG{kbR5g28?#wu99Ewcy7Yi)Sjqn0YISo}6{ z;%tl&s8KgTe7#4UH4g?NR}rlNHXtZ4Nxb{bRnZx68R0>Fvd53+oaB4#>d;cd5Uu;P zb=Z!z0&~Ecl!S0I#||jM$?Mc+F6Sh_L6IJ39vd9NO;%cc9d6&Ol1AEr&(cs===Fy%^rh;@f7ym4sH` z)fAeo24rgT%ImmGnUA>&)d$oA?(aj~-1s%qZ=NyG{_@e)b7x*OuO@O@IY#qO>XHJDz}Tl|BX5 z7*ot^n7tf{YCfnhm+keYLp{i}+oCK@!T~B`(u|HY9cJX4rTk%I&?58CPFPr zYpBrqwPJl~1L?|H@pcGumyio(DsjxMktm|6w|kii^mvmn-~u$+$qcVa9Q3lL%5_3i z*xyV6KBS6RUt@AbJk_Z`;s1}NcKxd)Zlh7HxpS0_iW7I8{(Q{MyOpSfUS4w3o})rH zH#XWti8A)MX>F0LJpKt#yVCV{VxLe)n@@F0;HL|HUgrp(q+H!NE@MtW6mgn&Uub!& z9Y!Y9k5LO=D<4*<;CtH!fK+uQ=L?uc6ivtV<@vn0Wmb?TegZbZeF^lawb1&0FnPTq zE9A|aUq@fQh-aoXtFNc_d-XCF8y+;;9qpl3)tp&$CO}bS7R0HkhPI=Jn!;d0QUjHq zM`4(53wr{Uz8j9opS2954XzYw6eE|eiz;o*a`fK4=l$x{Kx)zz8Dy})fMEhm6JS!% zmPjLrAl@IO%1T%E`PT;a_E|&a-A=H#F7zr?&V17imxT&p^6$=jaNiS5Ns{fsz(;eQaG(-)dbz^94W$(r^QzI*$Nu{;6)a4JwFOc1fU=bJoV0PYyH+xY zaX-Yv3bu0%y>T2bV#{)U)xTG%UuGDWIENz}auE_2kNZiozqt(-IN!sDe@XNiSAC5+uFa0i1<8_QC=o@En}fWwcQx8qKh{fqh*u)(n3d13_x^ zRb7lU41-t=;7(aCX7Z= z^dFi5ZSaskZotXd_xwoPpkDl~LRLx>q~}@3Dp7P}Moh^O8oa_XI0TU7eRu{dqHMWM z{5S~B*K6PazTDG!fngg3c#}Jl;`p#_UqocG#hpH9I`R|GKq0eBbTcTUd066M>9T{njdU74%%I|qw6hPcKB$1jfi)W(qjL|9jrR##H)x7wU%1+4^InjNw=l%9gm63bhMRFjvwze6@{#`BjG6GxMtkUlF( zz3oRg32n+y$8AAyq^64Gp_l+xQyJwrPmz51XXE-_5an!DNar-(5BnC}uk zQ9sJNqVqOU>HYh&d8|na^Z7!1!tmh!x~M@L-wdi0ug-}k>}NDzuRPQ6eq0k;I?2zg z!9Emz^s00od%RLs2Da5O`!2KliiWRD+DI?fF_~>11Y$Q?!%mU$Fgu9jszF3+Zu@Un z%dGtPS#&Qtz`9V{2Yawe`6^T1sH8^~ls@LzS6J*#e-j)Ct%{*E#2RejH)RKeQ(x<% zCjYzRiorR*kZGTxSAoD3TPA5&xM)0a6J|Oy5&hC4VpF0j6eAmN@_f#Zk%>O^Q@na> zLKSDtj;{AXpY}ms$8QPB95{X{jQak~xR*711-$5Ap2)zB!;?ye!M)!iI&h$XUX{{u zur<7z()?rhJ<(Z%r9Bo;cn^bPJLi}c-B=!Srt$0W9Gh@F5CcPINRIU68NVmCEXZ4Y z&#l5OMIEP@41_-KdNOXa+!i+IwO>U>e?d3GQ8f|!)7&WJ8L8_C{p3{$2}x$^4Pf!- z(_1t{H80Om0OmC4|fz%B9fu}Gr3Z8!o<78BlYjcr}(^( zKAp#VnLh50TNwf7lcuBdKgX`M@_UtrLhn|mDmuR@F!655<*w~0t)EXA`iRO5usHc& zA)*A~R9-=<73Yu}t=Y1?=mYOCA~t6P944{;Km4Kb#&!U^Osi`;!L%-q&YEXzu}erOp98Z#r^ zyg~zZSe!onMriUp??6Cbq%mo#^#9Go&tO`>n=RNTVu0SGGH#}E|B}$ZQT~ZBbN9JK z%@rK^%HR#gxxDc~kG^`8^#5l^Reji?#+M#mmxVK*USl**{#{Q=xv68)a=WiWzI$w@ z@pGDu$yXDDDY8$Rn$b+8xd$jk5t6C~ST0Xu&c@5+qhFZlKn@DPe5Jf8_>act8gs^_ z$g55BDe~we^OBb~r^vg%{x|q{K6$a^{{4&z9K`HT%jGNVH64VKfa~T>zh)lP$QnrM zwK`v~tf!%bTk%@!28=s(EdMvc7IRQmh#QeRVcncnKts@!my5GUv((rwn`r`R>$F5a_uPNlP92E1i4YTPgWD=Db%QM)DCPnFjxjtM5 z3^SpB%!jeV2StjWFDXm%{b!B9T(kw6n*+K;lvVm5QEIRjh<=4#-l^^0<8{a{4>*PA z@-V4M8cF+Zd$>3oS1O78=(1J}>z$G+>IQvQmx(>0CV4cMPxGEZC zkL&_;6%?vNl23gCqSp9#sb18`R`IOJy!ik3=%a1(_{ZyVM^s=InD;nML&lkfcpcM` zj89^Ts5vAOu$sI(WTIs*{D%oXaP%>7{o z@mS;qC|v;VVR4jB+j&*_f_73`gNU-^prjn7H%p^09(Vw8W+1buN^pa913V2-?7AM@ zkHs9LHQ=CrKj+5`FjeRM82vOa65@eR70bcp)8yMK*Yea*x#bK-e15I`H(R1p4Vp8B zxxgpa}(d`Lu^&r%-zHXD8yY3 zZ-OS|W8z-PBkaTwVuun}85f@0Z8=VCtf|jWNlJuAu)EaaC6DrWq7U)5=+Zesn>KUV zHO;v^@-^7>hV^yjPwWpu{4U*R5{0{l1e9F;c zw>@vnVsUTPI%NTvHLXtc7)Kr3w%^oQuQ&&g46CVn)jCk84n8Y7ZL}Js(E_P}7Z?rLA)b}gytyftfEmgQdXE1n(NTewJO!JvRHCF9Q>HJaPY8b%F}X2vOnqzRsdFk z&9)KZ>;RK}4L0`1#h+t4w5kg50cQ{Wplf4C&468n66>QBuZcBrB7RY=0M_#KQTK*) zu`OaOOgMi}ec-7=x_EV>fBk>~V*xJyoVV#i4_Lg_yvQi(fvD+h3hjnoinV`Y-_>5w z?z;ugKJ9kWU^I^T!*!CT5!6cUPo{@DMga=Af!KgJ`H>3n6lOS zh1}UhwmlJo&S|^E*>C6zTlIX8n0i6OTGRh@s3AiRmJ4E3l3mvQdmii0AQO1*+W}Y{c^k`MC1yq|RJ>2wU z>(@kn#QPrZcDJfTIc%1})Zx-5W_H2PjlvF?MqQ~X)}m*sb&eRcWY$$4N-3g1zb)n-i;G6`(mjgQu`?)3e_WW|pA(^T@?lf{Zn>4d$tS zA~NT-(2!tW&*@3re9=FqH>C3j{H*08o$Oq%x|s$`G1RTFQE0 z$OYIce0_s{ZnwijNjK6isDXS=aUTwP^>Bn+)aqjw+S%xj_vUl^$oP0AQJ8w*D=C$Y zHNevIy)$T*DR(Mx)o(sb=>VFF3!~4z!90M;un+q79T>6<%J*3G@trL{(Wl5YK}0%) zIDK*4)R*Ut=L9^p7Or2qT`imAi-!R@QzeVQ{rw)@Am-qtK3+2_nMU&Q z?Dk%rI%<*hrxW9f-=#O6s<4Tp;?p-yXizL8Zyfvs#ZM~kwL!@3u3!YB__`6(Vqib` zVLBJU7&R;U5#Vz7p^ZU>-(r=x#AW-5a@6!dNO!8RFig=vh=suHPyF_*ElzPC2KZAK+G166Lty(9R0ZruGpKEI zeUOF{q}&O^S|fpBn;oI~59X$mWE5a9qfQ>~OD>U7|vl=LhH8i>~742tNu zc4Ijl_#7u!k+=;r?W)SNDrj3D3tb{bfkLbKOZt!sE+_=-j`gN5@J{y5m8==@N)AN@*VLVLG02k zS`@9a6^2{QfiG+*`Z)JJJ7GSYhwTSzX$Km)bPh~Q{Ui69>x(0MP`ANvLcda6%P`as z62HNN$B-x&PbrlozPtgl_^ra>D&sUgHDVs;m!}kGj#wqwAXE~XjFjpCi^=K12b;x= z;Mc(zhI${4Z)zdKa(nEhId}%zmi*7loP5p10n0p9q7sU%Xsx!A*8-IjRi5CiYasjo zYu09dbn))d`padKLtcZ^JmIz%z7IsbqOm=*G1D}RX$%=_Ac9wW;TxaC-Z;vxM3)^> zDQ+4cmn66nts~ezZkoCcoN()B{CA)CeLQXDUnAvWKRD6gOsDV_zk?o)6JvLF#{vP0 zx1a>o5QO=W@4R0XFl>O=`uZ2%xq|V3eR5bqT#rf!vf;b50{k-)S*yBygWy!1amRL; zT;EXKfcgXnZjU?EUYQeXpD|W32C@c5cbd_?>n%lv~@+=Kq5Klp$rI6 zSZ{<-^|6Dkm&NeBg<+=$rG4pE88YBo*25;Gq+GAFhH9j#L_O;rsebokOzLEO#zMK($fAQ?p9 zzfE1!P$jx82s{>K0lXG)LJWQP(GsQv6TK3c<1u@oZPfX?i>o6ra?v(FKkbO-Slhl`m~>CaI9La$6{r+>nN3$3 zov$F^`E>SWR3)A2WX*&&m{4uNTNUOiIR=~7K6n_V`M{)NLoFxc1{4v*o zpjQUdB9Bh2x*F)LB8p0$4vT5=^mv@JzQO8XxlpRYn*HVw-wbc#*DWwA&yA+~`1$ka z?@ge?_E>r}oPBQXgkVal(zep0ORo{Fvz~_Ac%0&v%q1`IH0mwHy6`UTZ4HoT|6mb? zU>7RZ2I%(}4#beUIcEV~3R*?1n2ZP_Oi;AKKGY58F?mBrzazGXM2YPL%PvWqGw?B&rS%X9F96gl)7V8UFhKZD?7%JZ3b_sFZ77 zx-q9YTIgrinR!dVzZ+kv=-bI%VphfL*S!2oNq)X!{~^zbK%>ta=pr%^yT@}u;L6JF zdeZ3^3lHp_z|51{53Lm3XUnXhlHkP`CGqP zf%*3TjP1_-{1J6;`Cm6;>fece>CR9#=}f(uFDutt#LIRHJN!GT>6L328@o!bV1x2F@&7oFRI2)xC#-QNYeK^sd{^7Itr;lAk9EfIC zE3G0rq&Pz-k2_RH^rNSdcUYA;gCpxF=~h2rstc!->-kkVthkICuN7RQG1zqoCuCRciU5SbqXkQPkE0p zIbtj5KR0)Qi6 zSxVzgB3=c%+p+hMcD)nderYJ)llOnChm7Y9(8mqKvZW1QGy5yv zfyHGLBzl#YmSA_BZAbe?fuIFE9rm!D`}P$+_X!uxK3Z?jn;y2mU6vQz?QX9SZ<{do zz%Q+Xq7#z)N9mi#YlUyqr|9e`G#b`3EWIb4^d0EmN&LQ7VqMOlUnXx5pmN}#ezAW* z869)S64+|%w+41|T)q&Gy9BRg@e=kTZ_utQyjFW+Om%JDtf_4o5IFqOXN05ZiwBi* zQ9M6=8k;=Va&1jvHQNgTtVM_$d?}~%nX<>LyX`%yke!7h7hC#tb|q-a6a%~$9lj_t zO&WcW`8;d{d%)b;+`{qu!9B}Ho~^?Q3H?UIP0BBd+Ed7}F*tJp`oQ;nvJZ`DJ!`p^ z;NplHvz*C0E6{>I)tBFDN|vDc&TyGY`c|a=E_w0;JhS)H#L_v<_kMQCGmXk}^S!%_ z3B)(GNs`hEY?ta_W&TdI=H3&8R)9Kxdylde#US^$z< diff --git a/docs/tutorials/avro/training.avsc b/docs/tutorials/avro/training.avsc deleted file mode 100644 index 31ce0aef4..000000000 --- a/docs/tutorials/avro/training.avsc +++ /dev/null @@ -1 +0,0 @@ -{"name": "ImageDataset", "type": "record", "fields": [{"name": "features", "type": {"type": "array", "items": "int"}}, {"name": "label", "type": "int"}, {"name": "dataType", "type": {"type": "enum", "name": "dataTypes", "symbols": ["TRAINING", "VALIDATION"]}}]} \ No newline at end of file From 337ef967eb53897f04cc698593f394114b583445 Mon Sep 17 00:00:00 2001 From: Cheng Ren Date: Tue, 19 Jan 2021 15:02:21 -0800 Subject: [PATCH 40/85] Add tutorial for avro dataset API (#1250) --- docs/tutorials/avro.ipynb | 576 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 576 insertions(+) create mode 100644 docs/tutorials/avro.ipynb diff --git a/docs/tutorials/avro.ipynb b/docs/tutorials/avro.ipynb new file mode 100644 index 000000000..f34c81503 --- /dev/null +++ b/docs/tutorials/avro.ipynb @@ -0,0 +1,576 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "Tce3stUlHN0L" + }, + "source": [ + "##### Copyright 2020 The TensorFlow IO Authors." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "cellView": "form", + "id": "tuOe1ymfHZPu" + }, + "outputs": [], + "source": [ + "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "# you may not use this file except in compliance with the License.\n", + "# You may obtain a copy of the License at\n", + "#\n", + "# https://www.apache.org/licenses/LICENSE-2.0\n", + "#\n", + "# Unless required by applicable law or agreed to in writing, software\n", + "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "# See the License for the specific language governing permissions and\n", + "# limitations under the License." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "qFdPvlXBOdUN" + }, + "source": [ + "# Avro Dataset API" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "MfBg1C5NB3X0" + }, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
    \n", + " View on TensorFlow.org\n", + " \n", + " Run in Google Colab\n", + " \n", + " View source on GitHub\n", + " \n", + " Download notebook\n", + "
    " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "xHxb-dlhMIzW" + }, + "source": [ + "## Overview\n", + "\n", + "The objective of Avro Dataset API is to load Avro formatted data natively into TensorFlow as
    TensorFlow dataset. Avro is a data serialization system similiar to Protocol Buffers. It's widely used in Apache Hadoop where it can provide both a serialization format for persistent data, and a wire format for communication between Hadoop nodes. Avro data is a row-oriented, compacted binary data format. It relies on schema which is stored as a separate JSON file. For the spec of Avro format and schema declaration, please refer to
    the official manual.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "MUXex9ctTuDB" + }, + "source": [ + "## Setup package\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "upgCc3gXybsA" + }, + "source": [ + "### Install the required tensorflow-io package" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "uUDYyMZRfkX4" + }, + "outputs": [], + "source": [ + "!pip install tensorflow-io" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "gjrZNJQRJP-U" + }, + "source": [ + "### Import packages" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "id": "m6KXZuTBWgRm" + }, + "outputs": [], + "source": [ + "import tensorflow as tf\n", + "import tensorflow_io as tfio\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "eCgO11GTJaTj" + }, + "source": [ + "### Validate tf and tfio imports" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "id": "dX74RKfZ_TdF" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "tensorflow-io version: 0.17.0\n", + "tensorflow version: 2.4.0\n" + ] + } + ], + "source": [ + "print(\"tensorflow-io version: {}\".format(tfio.__version__))\n", + "print(\"tensorflow version: {}\".format(tf.__version__))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "J0ZKhA6s0Pjp" + }, + "source": [ + "## Usage" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "4CfKVmCvwcL7" + }, + "source": [ + "### Explore the dataset\n", + "\n", + "For the purpose of this tutorial, let's download the sample Avro dataset. \n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "IGnbXuVnSo8T" + }, + "source": [ + "Download a sample Avro file:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Tu01THzWcE-J" + }, + "outputs": [], + "source": [ + "!curl -OL https://github.com/tensorflow/io/raw/master/docs/tutorials/avro/train.avro\n", + "!ls -l train.avro" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "IGnbXuVnSo8T" + }, + "source": [ + "Download the corresponding schema file of the sample Avro file:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Tu01THzWcE-J" + }, + "outputs": [], + "source": [ + "!curl -OL https://github.com/tensorflow/io/raw/master/docs/tutorials/avro/train.avsc\n", + "!ls -l train.avsc" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "z9GCyPWNuOm7" + }, + "source": [ + "In the above example, a testing Avro dataset were created based on mnist dataset. The original mnist dataset in TFRecord format is generated from TF named dataset. However, the mnist dataset is too large as a demo dataset. For simplicity purpose, most of it were trimmed and first few records only were kept. Moreover, additional trimming was done for `image` field in original mnist dataset and mapped it to `features` field in Avro. So the avro file `train.avro` has 4 records, each of which has 3 fields: `features`, which is an array of int, `label`, an int or null, and `dataType`, an enum. To view the decoded `train.avro` (Note the original avro data file is not human readable as avro is a compacted format):\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "upgCc3gXybsB" + }, + "source": [ + "Install the required package to read Avro file:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "nS3eTBvjt-O4" + }, + "outputs": [], + "source": [ + "!pip install avro\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "upgCc3gXybsB" + }, + "source": [ + "To read and print an Avro file in a human-readable format:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "nS3eTBvjt-O5" + }, + "outputs": [], + "source": [ + "from avro.io import DatumReader\n", + "from avro.datafile import DataFileReader\n", + "\n", + "import json\n", + "\n", + "def print_avro(avro_file, max_record_num=None):\n", + " if max_record_num is not None and max_record_num <= 0:\n", + " return\n", + "\n", + " with open(avro_file, 'rb') as avro_handler:\n", + " reader = DataFileReader(avro_handler, DatumReader())\n", + " record_count = 0\n", + " for record in reader:\n", + " record_count = record_count+1\n", + " print(record)\n", + " if max_record_num is not None and record_count == max_record_num:\n", + " break\n", + "\n", + "print_avro(avro_file='train.avro')\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "z9GCyPWNuOm7" + }, + "source": [ + "And the schema of `train.avro` which is represented by `train.avsc` is a JSON-formatted file.\n", + "To view the `train.avsc`: \n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "nS3eTBvjt-O5" + }, + "outputs": [], + "source": [ + "def print_schema(avro_schema_file):\n", + " with open(avro_schema_file, 'r') as handle:\n", + " parsed = json.load(handle)\n", + " print(json.dumps(parsed, indent=4, sort_keys=True))\n", + "\n", + "print_schema('train.avsc')\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "4CfKVmCvwcL7" + }, + "source": [ + "### Prepare the dataset\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "z9GCyPWNuOm7" + }, + "source": [ + "Load `train.avro` as TensorFlow dataset with Avro dataset API: \n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "nS3eTBvjt-O5" + }, + "outputs": [], + "source": [ + "features = {\n", + " 'features[*]': tfio.experimental.columnar.VarLenFeatureWithRank(dtype=tf.int32),\n", + " 'label': tf.io.FixedLenFeature(shape=[], dtype=tf.int32, default_value=-100),\n", + " 'dataType': tf.io.FixedLenFeature(shape=[], dtype=tf.string)\n", + "}\n", + "\n", + "schema = tf.io.gfile.GFile('train.avsc').read()\n", + "\n", + "dataset = tfio.experimental.columnar.make_avro_record_dataset(file_pattern=['train.avro'],\n", + " reader_schema=schema,\n", + " features=features,\n", + " shuffle=False,\n", + " batch_size=3,\n", + " num_epochs=1)\n", + "\n", + "for record in dataset:\n", + " print(record['features[*]'])\n", + " print(record['label'])\n", + " print(record['dataType'])\n", + " print(\"--------------------\")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "IF_kYz_o2DH4" + }, + "source": [ + "The above example converts `train.avro` into tensorflow dataset. Each element of the dataset is a dictionary whose key is the feature name, value is the converted sparse or dense tensor. \n", + "E.g, it converts `features`, `label`, `dataType` field to a VarLenFeature(SparseTensor), FixedLenFeature(DenseTensor), and FixedLenFeature(DenseTensor) respectively. Since batch_size is 3, it coerce 3 records from `train.avro` into one element in the result dataset.\n", + "For the first record in `train.avro` whose label is null, avro reader replaces it with the specified default value(-100).\n", + "In this example, there're 4 records in total in `train.avro`. Since batch size is 3, the result dataset contains 3 elements, last of which's batch size is 1. However user is also able to drop the last batch if the size is smaller than batch size by enabling `drop_final_batch`. E.g: \n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "nS3eTBvjt-O5" + }, + "outputs": [], + "source": [ + "dataset = tfio.experimental.columnar.make_avro_record_dataset(file_pattern=['train.avro'],\n", + " reader_schema=schema,\n", + " features=features,\n", + " shuffle=False,\n", + " batch_size=3,\n", + " drop_final_batch=True,\n", + " num_epochs=1)\n", + "\n", + "for record in dataset:\n", + " print(record)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "IF_kYz_o2DH4" + }, + "source": [ + "One can also increase num_parallel_reads to expediate Avro data processing by increasing avro parse/read parallelism.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "nS3eTBvjt-O5" + }, + "outputs": [], + "source": [ + "dataset = tfio.experimental.columnar.make_avro_record_dataset(file_pattern=['train.avro'],\n", + " reader_schema=schema,\n", + " features=features,\n", + " shuffle=False,\n", + " num_parallel_reads=16,\n", + " batch_size=3,\n", + " drop_final_batch=True,\n", + " num_epochs=1)\n", + "\n", + "for record in dataset:\n", + " print(record)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "IF_kYz_o2DH4" + }, + "source": [ + "For detailed usage of `make_avro_record_dataset`, please refer to API doc.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "4CfKVmCvwcL7" + }, + "source": [ + "### Train tf.keras models with Avro dataset\n", + "\n", + "Now let's walk through an end-to-end example of tf.keras model training with Avro dataset based on mnist dataset.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "z9GCyPWNuOm7" + }, + "source": [ + "Load `train.avro` as TensorFlow dataset with Avro dataset API: \n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "nS3eTBvjt-O5" + }, + "outputs": [], + "source": [ + "features = {\n", + " 'features[*]': tfio.experimental.columnar.VarLenFeatureWithRank(dtype=tf.int32)\n", + "}\n", + "\n", + "schema = tf.io.gfile.GFile('train.avsc').read()\n", + "\n", + "dataset = tfio.experimental.columnar.make_avro_record_dataset(file_pattern=['train.avro'],\n", + " reader_schema=schema,\n", + " features=features,\n", + " shuffle=False,\n", + " batch_size=1,\n", + " num_epochs=1)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "z9GCyPWNuOm7" + }, + "source": [ + "Define a simple keras model: \n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "id": "m6KXZuTBWgRm" + }, + "outputs": [], + "source": [ + "def build_and_compile_cnn_model():\n", + " model = tf.keras.Sequential()\n", + " model.compile(optimizer='sgd', loss='mse')\n", + " return model\n", + "\n", + "model = build_and_compile_cnn_model()\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "4CfKVmCvwcL7" + }, + "source": [ + "### Train the keras model with Avro dataset:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "id": "m6KXZuTBWgRm" + }, + "outputs": [], + "source": [ + "model.fit(x=dataset, epochs=1, steps_per_epoch=1, verbose=1)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "IF_kYz_o2DH4" + }, + "source": [ + "The avro dataset can parse and coerce any avro data into TensorFlow tensors, including records in records, maps, arrays, branches, and enumerations. The parsing information is passed into the avro dataset implementation as a map where \n", + "keys encode how to parse the data \n", + "values encode on how to coerce the data into TensorFlow tensors – deciding the primitive type (e.g. bool, int, long, float, double, string) as well as the tensor type (e.g. sparse or dense). A listing of TensorFlow's parser types (see Table 1) and the coercion of primitive types (Table 2) is provided. \n", + "\n", + "Table 1 the supported TensorFlow parser types:\n", + "\n", + "TensorFlow Parser Types|TensorFlow Tensors|Explanation\n", + "----|----|------\n", + "tf.FixedLenFeature([], tf.int32)|dense tensor|Parse a fixed length feature; that is all rows have the same constant number of elements, e.g. just one element or an array that has always the same number of elements for each row \n", + "tf.SparseFeature(index_key=['key_1st_index', 'key_2nd_index'], value_key='key_value', dtype=tf.int64, size=[20, 50]) |sparse tensor|Parse a sparse feature where each row has a variable length list of indices and values. The 'index_key' identifies the indices. The 'value_key' identifies the value. The 'dtype' is the data type. The 'size' is the expected maximum index value for each index entry\n", + "tfio.experimental.columnar.VarLenFeatureWithRank([],tf.int64) |sparse tensor|Parse a variable length feature; that means each data row can have a variable number of elements, e.g. the 1st row has 5 elements, the 2nd row has 7 elements\n", + "\n", + "Table 2 the supported conversion from Avro types to TensorFlow's types:\n", + "\n", + "Avro Primitive Type|TensorFlow Primitive Type\n", + "----|----\n", + "boolean: a binary value|tf.bool\n", + "bytes: a sequence of 8-bit unsigned bytes|tf.string\n", + "double: double precision 64-bit IEEE floating point number|tf.float64\n", + "enum: enumeration type|tf.string using the symbol name\n", + "float: single precision 32-bit IEEE floating point number|tf.float32\n", + "int: 32-bit signed integer|tf.int32\n", + "long: 64-bit signed integer|tf.int64\n", + "null: no value|uses default value\n", + "string: unicode character sequence|tf.string\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "IF_kYz_o2DH4" + }, + "source": [ + "A comprehensive set of examples of Avro dataset API is provided within the tests.\n" + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "collapsed_sections": [ + "Tce3stUlHN0L" + ], + "name": "avro.ipynb", + "toc_visible": true + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} From c0d56ee498c5564790ad3a9760493fb377a4aaac Mon Sep 17 00:00:00 2001 From: Vignesh Kothapalli Date: Wed, 20 Jan 2021 22:10:36 +0530 Subject: [PATCH 41/85] remove docker based mongodb tests in macos (#1279) --- .github/workflows/build.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 898100826..7d6ad281e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -180,7 +180,6 @@ jobs: bash -x -e tests/test_azure/start_azure.sh bash -x -e tests/test_pubsub/pubsub_test.sh bash -x -e tests/test_pulsar/pulsar_test.sh - bash -x -e tests/test_mongodb/mongodb_test.sh start - name: Install ${{ matrix.python }} macOS run: | set -x -e From f16c61390a8972358f2bc87b02bac4b6c4ee0ad3 Mon Sep 17 00:00:00 2001 From: Vignesh Kothapalli Date: Tue, 26 Jan 2021 07:09:30 +0530 Subject: [PATCH 42/85] trigger benchmarks workflow only on commits (#1282) --- .github/workflows/benchmarks.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index bff8936b0..67619432d 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -4,11 +4,8 @@ on: push: branches: - master - schedule: - - cron: "0 12 * * *" jobs: - macos: name: macOS ${{ matrix.python }} + ${{ matrix.version }} runs-on: macos-latest From ef2927c4607b22bd32cee516528716951211d799 Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Tue, 26 Jan 2021 21:48:15 -0800 Subject: [PATCH 43/85] Bump Apache Arrow to 3.0.0 (#1285) Signed-off-by: Yong Tang --- .github/workflows/build.wheel.sh | 2 +- WORKSPACE | 8 ++++---- third_party/arrow.BUILD | 3 ++- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.wheel.sh b/.github/workflows/build.wheel.sh index 78177066e..9753d7990 100755 --- a/.github/workflows/build.wheel.sh +++ b/.github/workflows/build.wheel.sh @@ -6,7 +6,7 @@ run_test() { entry=$1 CPYTHON_VERSION=$($entry -c 'import sys; print(str(sys.version_info[0])+str(sys.version_info[1]))') (cd wheelhouse && $entry -m pip install tensorflow_io-*-cp${CPYTHON_VERSION}-*.whl) - $entry -m pip install -q pytest pytest-benchmark boto3 fastavro avro-python3 scikit-image pandas pyarrow==2.0.0 google-cloud-pubsub==2.1.0 google-cloud-bigquery-storage==1.1.0 google-cloud-bigquery==2.3.1 google-cloud-storage==1.32.0 + $entry -m pip install -q pytest pytest-benchmark boto3 fastavro avro-python3 scikit-image pandas pyarrow==3.0.0 google-cloud-pubsub==2.1.0 google-cloud-bigquery-storage==1.1.0 google-cloud-bigquery==2.3.1 google-cloud-storage==1.32.0 (cd tests && $entry -m pytest --benchmark-disable -v --import-mode=append $(find . -type f \( -iname "test_*.py" ! \( -iname "test_*_eager.py" \) \))) (cd tests && $entry -m pytest --benchmark-disable -v --import-mode=append $(find . -type f \( -iname "test_*_eager.py" ! \( -iname "test_bigquery_eager.py" \) \))) # GRPC and test_bigquery_eager tests have to be executed separately because of https://github.com/grpc/grpc/issues/20034 diff --git a/WORKSPACE b/WORKSPACE index 1a46473be..27d4ca2a4 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -267,11 +267,11 @@ http_archive( http_archive( name = "arrow", build_file = "//third_party:arrow.BUILD", - sha256 = "ea299df9cf440cfc43393ce12ee6d9a4c9d0dfa9fde33c3bc9b70ec25520a844", - strip_prefix = "arrow-apache-arrow-2.0.0", + sha256 = "fc461c4f0a60e7470a7c58b28e9344aa8fb0be5cc982e9658970217e084c3a82", + strip_prefix = "arrow-apache-arrow-3.0.0", urls = [ - "https://storage.googleapis.com/mirror.tensorflow.org/github.com/apache/arrow/archive/apache-arrow-2.0.0.tar.gz", - "https://github.com/apache/arrow/archive/apache-arrow-2.0.0.tar.gz", + "https://storage.googleapis.com/mirror.tensorflow.org/github.com/apache/arrow/archive/apache-arrow-3.0.0.tar.gz", + "https://github.com/apache/arrow/archive/apache-arrow-3.0.0.tar.gz", ], ) diff --git a/third_party/arrow.BUILD b/third_party/arrow.BUILD index fff11cd50..85a5a5969 100644 --- a/third_party/arrow.BUILD +++ b/third_party/arrow.BUILD @@ -31,9 +31,10 @@ genrule( srcs = ["cpp/src/arrow/util/config.h.cmake"], outs = ["cpp/src/arrow/util/config.h"], cmd = ("sed " + - "-e 's/@ARROW_VERSION_MAJOR@/2/g' " + + "-e 's/@ARROW_VERSION_MAJOR@/3/g' " + "-e 's/@ARROW_VERSION_MINOR@/0/g' " + "-e 's/@ARROW_VERSION_PATCH@/0/g' " + + "-e 's/cmakedefine ARROW_USE_NATIVE_INT128/undef ARROW_USE_NATIVE_INT128/g' " + "-e 's/cmakedefine/define/g' " + "$< >$@"), ) From 03b77deb7a571cf49bce10be268292258bf54ecb Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Thu, 28 Jan 2021 18:54:15 -0800 Subject: [PATCH 44/85] Add bazel cache (#1287) Signed-off-by: Yong Tang --- .github/workflows/build.yml | 40 ++++++++++++++++++++++++++++++++----- docs/development.md | 20 +++++++++++++++---- 2 files changed, 51 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7d6ad281e..2ff75b978 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,6 +9,8 @@ on: - master env: + GCP_CREDS: ${{ secrets.GCP_CREDS }} + EVENT_NAME: ${{ github.event_name }} BAZEL_OPTIMIZATION: --copt=-msse4.2 --copt=-mavx --compilation_mode=opt jobs: @@ -49,6 +51,10 @@ jobs: steps: - uses: actions/checkout@v2 - run: | + if [[ "${EVENT_NAME}" == "push" ]]; then + printf '%s\n' "${GCP_CREDS}" >service_account_creds.json + export BAZEL_OPTIMIZATION="--remote_cache=https://storage.googleapis.com/tensorflow-sigs-io --remote_upload_local_results=true --google_credentials=service_account_creds.json" + fi set -x -e echo "Bring /usr/bin to front as GitHub does not use system python3 by default" export PATH=/usr/bin:$PATH @@ -65,12 +71,17 @@ jobs: steps: - uses: actions/checkout@v2 - run: | + if [[ "${EVENT_NAME}" == "push" ]]; then + printf '%s\n' "${GCP_CREDS}" >service_account_creds.json + export BAZEL_OPTIMIZATION="--remote_cache=https://storage.googleapis.com/tensorflow-sigs-io --remote_upload_local_results=true --google_credentials=service_account_creds.json" + fi set -x -e bash -x -e .github/workflows/build.space.sh python3 .github/workflows/build.instruction.py docs/development.md "##### Ubuntu 20.04" > source.sh cat source.sh - docker run -i --rm -v $PWD:/v -w /v --net=host ubuntu:20.04 \ - bash -x -e source.sh + docker run -i --rm -v $PWD:/v -w /v --net=host \ + -e BAZEL_OPTIMIZATION="${BAZEL_OPTIMIZATION}" \ + ubuntu:20.04 bash -x -e source.sh centos-7: name: CentOS 7 @@ -78,12 +89,17 @@ jobs: steps: - uses: actions/checkout@v2 - run: | + if [[ "${EVENT_NAME}" == "push" ]]; then + printf '%s\n' "${GCP_CREDS}" >service_account_creds.json + export BAZEL_OPTIMIZATION="--remote_cache=https://storage.googleapis.com/tensorflow-sigs-io --remote_upload_local_results=true --google_credentials=service_account_creds.json" + fi set -x -e bash -x -e .github/workflows/build.space.sh python3 .github/workflows/build.instruction.py docs/development.md "##### CentOS 7" > source.sh cat source.sh - docker run -i --rm -v $PWD:/v -w /v --net=host centos:7 \ - bash -x -e source.sh + docker run -i --rm -v $PWD:/v -w /v --net=host \ + -e BAZEL_OPTIMIZATION="${BAZEL_OPTIMIZATION}" \ + centos:7 bash -x -e source.sh macos-bazel: name: Bazel macOS @@ -92,6 +108,10 @@ jobs: - uses: actions/checkout@v2 - name: Bazel on macOS run: | + if [[ "${EVENT_NAME}" == "push" ]]; then + printf '%s\n' "${GCP_CREDS}" >service_account_creds.json + export BAZEL_OPTIMIZATION="${BAZEL_OPTIMIZATION} --remote_cache=https://storage.googleapis.com/tensorflow-sigs-io --remote_upload_local_results=true --google_credentials=service_account_creds.json" + fi set -x -e echo "Bring /usr/bin to front as GitHub does not use system python3 by default" export PATH=/usr/bin:$PATH @@ -199,6 +219,10 @@ jobs: - uses: actions/checkout@v2 - name: Bazel on Linux run: | + if [[ "${EVENT_NAME}" == "push" ]]; then + printf '%s\n' "${GCP_CREDS}" >service_account_creds.json + export BAZEL_OPTIMIZATION="${BAZEL_OPTIMIZATION} --remote_cache=https://storage.googleapis.com/tensorflow-sigs-io --remote_upload_local_results=true --google_credentials=service_account_creds.json" + fi set -x -e bash -x -e .github/workflows/build.space.sh BAZEL_OS=$(uname | tr '[:upper:]' '[:lower:]') @@ -302,6 +326,12 @@ jobs: BAZEL_VC: "C:/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise/VC/" shell: cmd run: | + if "%EVENT_NAME%" == "push" ( + printenv GCP_CREDS > service_account_creds.json + set "BAZEL_OPTIMIZATION=--remote_cache=https://storage.googleapis.com/tensorflow-sigs-io --remote_upload_local_results=true --google_credentials=service_account_creds.json" + ) else ( + echo %EVENT_NAME% + ) @echo on set /P BAZEL_VERSION=< .bazelversion curl -sSL -o bazel.exe https://github.com/bazelbuild/bazel/releases/download/%BAZEL_VERSION%/bazel-%BAZEL_VERSION%-windows-x86_64.exe @@ -315,7 +345,7 @@ jobs: python3 setup.py --package-version | xargs python3 -m pip install python3 tools/build/configure.py cat .bazelrc - bazel build -s --verbose_failures //tensorflow_io/core:python/ops/libtensorflow_io.so //tensorflow_io/core:python/ops/libtensorflow_io_plugins.so + bazel build -s --verbose_failures %BAZEL_OPTIMIZATION% //tensorflow_io/core:python/ops/libtensorflow_io.so //tensorflow_io/core:python/ops/libtensorflow_io_plugins.so - uses: actions/upload-artifact@v1 with: name: ${{ runner.os }}-bazel-bin diff --git a/docs/development.md b/docs/development.md index 2e690f46a..c23c7d66a 100644 --- a/docs/development.md +++ b/docs/development.md @@ -77,8 +77,12 @@ sudo bash -x -e bazel-$(cat .bazelversion)-installer-darwin-x86_64.sh # Install tensorflow and configure bazel sudo ./configure.sh +# Add any optimization on bazel command, e.g., --compilation_mode=opt, +# --copt=-msse4.2, --remote_cache=, etc. +# export BAZEL_OPTIMIZATION= + # Build shared libraries -bazel build -s --verbose_failures //tensorflow_io/... +bazel build -s --verbose_failures $BAZEL_OPTIMIZATION //tensorflow_io/... # Once build is complete, shared libraries will be available in # `bazel-bin/tensorflow_io/core/python/ops/` and it is possible @@ -132,8 +136,12 @@ sudo python3 -m pip install -U pip # Install tensorflow and configure bazel sudo ./configure.sh +# Add any optimization on bazel command, e.g., --compilation_mode=opt, +# --copt=-msse4.2, --remote_cache=, etc. +# export BAZEL_OPTIMIZATION= + # Build shared libraries -bazel build -s --verbose_failures //tensorflow_io/... +bazel build -s --verbose_failures $BAZEL_OPTIMIZATION //tensorflow_io/... # Once build is complete, shared libraries will be available in # `bazel-bin/tensorflow_io/core/python/ops/` and it is possible @@ -182,10 +190,14 @@ scl enable rh-python36 devtoolset-9 \ scl enable rh-python36 devtoolset-9 \ './configure.sh' +# Add any optimization on bazel command, e.g., --compilation_mode=opt, +# --copt=-msse4.2, --remote_cache=, etc. +# export BAZEL_OPTIMIZATION= + # Build shared libraries, notice the passing of --//tensorflow_io/core:static_build BAZEL_LINKOPTS="-static-libstdc++ -static-libgcc" BAZEL_LINKLIBS="-lm -l%:libstdc++.a" \ scl enable rh-python36 devtoolset-9 \ - 'bazel build -s --verbose_failures --//tensorflow_io/core:static_build //tensorflow_io/...' + 'bazel build -s --verbose_failures $BAZEL_OPTIMIZATION --//tensorflow_io/core:static_build //tensorflow_io/...' # Once build is complete, shared libraries will be available in # `bazel-bin/tensorflow_io/core/python/ops/` and it is possible @@ -336,4 +348,4 @@ until_out_of_range({ batch <- sess$run(next_batch) print(batch) }) -``` \ No newline at end of file +``` From f492bc8fdca062118a0e60c7c1ccb0df9ae1844a Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Thu, 28 Jan 2021 19:19:27 -0800 Subject: [PATCH 45/85] Add initial bigtable stub test (#1286) * Add initial bigtable stub test Signed-off-by: Yong Tang * Fix kokoro test Signed-off-by: Yong Tang --- .github/workflows/build.wheel.sh | 2 +- .github/workflows/build.yml | 4 +- .kokorun/io_cpu.sh | 2 +- tests/test_bigtable_eager.py | 119 ++++++++++++++++++ .../test_pubsub_bigtable.sh} | 6 +- 5 files changed, 128 insertions(+), 5 deletions(-) create mode 100644 tests/test_bigtable_eager.py rename tests/{test_pubsub/pubsub_test.sh => test_gcloud/test_pubsub_bigtable.sh} (74%) diff --git a/.github/workflows/build.wheel.sh b/.github/workflows/build.wheel.sh index 9753d7990..502984e72 100755 --- a/.github/workflows/build.wheel.sh +++ b/.github/workflows/build.wheel.sh @@ -6,7 +6,7 @@ run_test() { entry=$1 CPYTHON_VERSION=$($entry -c 'import sys; print(str(sys.version_info[0])+str(sys.version_info[1]))') (cd wheelhouse && $entry -m pip install tensorflow_io-*-cp${CPYTHON_VERSION}-*.whl) - $entry -m pip install -q pytest pytest-benchmark boto3 fastavro avro-python3 scikit-image pandas pyarrow==3.0.0 google-cloud-pubsub==2.1.0 google-cloud-bigquery-storage==1.1.0 google-cloud-bigquery==2.3.1 google-cloud-storage==1.32.0 + $entry -m pip install -q pytest pytest-benchmark boto3 fastavro avro-python3 scikit-image pandas pyarrow==3.0.0 google-cloud-pubsub==2.1.0 google-cloud-bigtable==1.6.0 google-cloud-bigquery-storage==1.1.0 google-cloud-bigquery==2.3.1 google-cloud-storage==1.32.0 (cd tests && $entry -m pytest --benchmark-disable -v --import-mode=append $(find . -type f \( -iname "test_*.py" ! \( -iname "test_*_eager.py" \) \))) (cd tests && $entry -m pytest --benchmark-disable -v --import-mode=append $(find . -type f \( -iname "test_*_eager.py" ! \( -iname "test_bigquery_eager.py" \) \))) # GRPC and test_bigquery_eager tests have to be executed separately because of https://github.com/grpc/grpc/issues/20034 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2ff75b978..8abdb9b5a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -198,7 +198,7 @@ jobs: set -x -e bash -x -e tests/test_kafka/kafka_test.sh bash -x -e tests/test_azure/start_azure.sh - bash -x -e tests/test_pubsub/pubsub_test.sh + bash -x -e tests/test_gcloud/test_pubsub_bigtable.sh bash -x -e tests/test_pulsar/pulsar_test.sh - name: Install ${{ matrix.python }} macOS run: | @@ -296,7 +296,7 @@ jobs: bash -x -e .github/workflows/build.space.sh bash -x -e tests/test_kafka/kafka_test.sh bash -x -e tests/test_aws/aws_test.sh - bash -x -e tests/test_pubsub/pubsub_test.sh + bash -x -e tests/test_gcloud/test_pubsub_bigtable.sh bash -x -e tests/test_prometheus/prometheus_test.sh start bash -x -e tests/test_elasticsearch/elasticsearch_test.sh start bash -x -e tests/test_mongodb/mongodb_test.sh start diff --git a/.kokorun/io_cpu.sh b/.kokorun/io_cpu.sh index 9c62f914c..3539922e8 100755 --- a/.kokorun/io_cpu.sh +++ b/.kokorun/io_cpu.sh @@ -75,7 +75,7 @@ bash -x -e tests/test_gcloud/test_gcs.sh gcs-emulator bash -x -e tests/test_kafka/kafka_test.sh bash -x -e tests/test_pulsar/pulsar_test.sh bash -x -e tests/test_aws/aws_test.sh -bash -x -e tests/test_pubsub/pubsub_test.sh pubsub +bash -x -e tests/test_gcloud/test_pubsub_bigtable.sh bash -x -e tests/test_prometheus/prometheus_test.sh start bash -x -e tests/test_azure/start_azure.sh bash -x -e tests/test_sql/sql_test.sh sql diff --git a/tests/test_bigtable_eager.py b/tests/test_bigtable_eager.py new file mode 100644 index 000000000..6ec179f66 --- /dev/null +++ b/tests/test_bigtable_eager.py @@ -0,0 +1,119 @@ +# Copyright 2018 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +"""Stub Test""" + +import os +import sys +import time +import shutil +import datetime +import tempfile +import numpy as np +import pytest + +import tensorflow as tf +import tensorflow_io as tfio + + +def bigtable_func(project_id, instance_id, table_id): + from google.cloud import bigtable + from google.cloud.bigtable import column_family + from google.cloud.bigtable import row_filters + from google.auth.credentials import AnonymousCredentials + + os.environ["BIGTABLE_EMULATOR_HOST"] = "localhost:8086" + + # [START bigtable_hw_connect] + # The client must be created with admin=True because it will create a + # table. + client = bigtable.Client( + project=project_id, admin=True, credentials=AnonymousCredentials() + ) + instance = client.instance(instance_id) + # [END bigtable_hw_connect] + + # [START bigtable_hw_create_table] + print("Creating the {} table.".format(table_id)) + table = instance.table(table_id) + + print("Creating column family cf1 with Max Version GC rule...") + # Create a column family with GC policy : most recent N versions + # Define the GC policy to retain only the most recent 2 versions + max_versions_rule = column_family.MaxVersionsGCRule(2) + column_family_id = "cf1" + column_families = {column_family_id: max_versions_rule} + if not table.exists(): + table.create(column_families=column_families) + else: + print("Table {} already exists.".format(table_id)) + # [END bigtable_hw_create_table] + + # [START bigtable_hw_write_rows] + print("Writing some greetings to the table.") + greetings = ["Hello World!", "Hello Cloud Bigtable!", "Hello Python!"] + rows = [] + column = b"greeting" + for i, value in enumerate(greetings): + # Note: This example uses sequential numeric IDs for simplicity, + # but this can result in poor performance in a production + # application. Since rows are stored in sorted order by key, + # sequential keys can result in poor distribution of operations + # across nodes. + # + # For more information about how to design a Bigtable schema for + # the best performance, see the documentation: + # + # https://cloud.google.com/bigtable/docs/schema-design + row_key = "greeting{}".format(i).encode() + row = table.direct_row(row_key) + row.set_cell( + column_family_id, column, value, timestamp=datetime.datetime.utcnow() + ) + rows.append(row) + table.mutate_rows(rows) + # [END bigtable_hw_write_rows] + + # [START bigtable_hw_create_filter] + # Create a filter to only retrieve the most recent version of the cell + # for each column accross entire row. + row_filter = row_filters.CellsColumnLimitFilter(1) + # [END bigtable_hw_create_filter] + + # [START bigtable_hw_get_with_filter] + print("Getting a single greeting by row key.") + key = b"greeting0" + + row = table.read_row(key, row_filter) + cell = row.cells[column_family_id][column][0] + print(cell.value.decode("utf-8")) + # [END bigtable_hw_get_with_filter] + + # [START bigtable_hw_scan_with_filter] + print("Scanning for all greetings:") + partial_rows = table.read_rows(filter_=row_filter) + + for row in partial_rows: + cell = row.cells[column_family_id][column][0] + print(cell.value.decode("utf-8")) + # [END bigtable_hw_scan_with_filter] + + # [START bigtable_hw_delete_table] + print("Deleting the {} table.".format(table_id)) + table.delete() + # [END bigtable_hw_delete_table] + + +def test_bigtable(): + bigtable_func("bigtable_project", "bigtable_instance", "bigtable_table") diff --git a/tests/test_pubsub/pubsub_test.sh b/tests/test_gcloud/test_pubsub_bigtable.sh similarity index 74% rename from tests/test_pubsub/pubsub_test.sh rename to tests/test_gcloud/test_pubsub_bigtable.sh index c8a82f58a..ff504b924 100755 --- a/tests/test_pubsub/pubsub_test.sh +++ b/tests/test_gcloud/test_pubsub_bigtable.sh @@ -23,8 +23,10 @@ if [ "$#" -eq 1 ]; then echo pull google/cloud-sdk docker pull google/cloud-sdk:236.0.0 echo pull google/cloud-sdk successfully - docker run -d --rm --net=host --name=$container -v $base:/v -w /v google/cloud-sdk:236.0.0 bash -x -c 'gcloud beta emulators pubsub start' + docker run -d --rm --net=host --name=$container-pubsub -v $base:/v -w /v google/cloud-sdk:236.0.0 bash -x -c 'gcloud beta emulators pubsub start' echo wait 10 secs until pubsub is up and running + docker run -d --rm --net=host --name=$container-bigtable -v $base:/v -w /v google/cloud-sdk:236.0.0 bash -x -c 'gcloud beta emulators bigtable start' + echo wait 10 secs until bigtable is up and running sleep 10 exit 0 fi @@ -34,7 +36,9 @@ tar -xzf google-cloud-sdk-236.0.0-darwin-x86_64.tar.gz google-cloud-sdk/install.sh -q google-cloud-sdk/bin/gcloud -q components install beta google-cloud-sdk/bin/gcloud -q components install pubsub-emulator +google-cloud-sdk/bin/gcloud -q components update beta google-cloud-sdk/bin/gcloud -q beta emulators pubsub start & +google-cloud-sdk/bin/gcloud -q beta emulators bigtable start & exit 0 From 2808ac3223fa2c5a162bce6ee681538def6ce663 Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Fri, 29 Jan 2021 20:05:10 -0800 Subject: [PATCH 46/85] Update azure lite v0.3.0 (#1288) Signed-off-by: Yong Tang --- WORKSPACE | 8 ++++---- tensorflow_io/core/plugins/az/az_file_system.cc | 2 +- third_party/azure.BUILD | 1 - 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/WORKSPACE b/WORKSPACE index 27d4ca2a4..69b5710d1 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -180,11 +180,11 @@ http_archive( "echo '' >> include/base64.h", "echo '#include ' >> include/base64.h", ], - sha256 = "597d9894061f4871a909f1c2c3f56725a69c188ea17784cc71e1e170687faf00", - strip_prefix = "azure-storage-cpplite-0.2.0", + sha256 = "25f34354fb0400ffe1b5a5c09c793c9fc8104d375910f6c84ab10fa50c0059cb", + strip_prefix = "azure-storage-cpplite-0.3.0", urls = [ - "https://storage.googleapis.com/mirror.tensorflow.org/github.com/Azure/azure-storage-cpplite/archive/v0.2.0.tar.gz", - "https://github.com/Azure/azure-storage-cpplite/archive/v0.2.0.tar.gz", + "https://storage.googleapis.com/mirror.tensorflow.org/github.com/Azure/azure-storage-cpplite/archive/v0.3.0.tar.gz", + "https://github.com/Azure/azure-storage-cpplite/archive/v0.3.0.tar.gz", ], ) diff --git a/tensorflow_io/core/plugins/az/az_file_system.cc b/tensorflow_io/core/plugins/az/az_file_system.cc index ddf0aefd4..18ff9b7f2 100644 --- a/tensorflow_io/core/plugins/az/az_file_system.cc +++ b/tensorflow_io/core/plugins/az/az_file_system.cc @@ -161,7 +161,7 @@ std::string errno_to_string() { case container_delete_fail: return "container_delete_fail"; /* blob level */ - case blob__already_exists: + case blob_already_exists: return "blob__already_exists"; case blob_not_exists: return "blob_not_exists"; diff --git a/third_party/azure.BUILD b/third_party/azure.BUILD index 4c11b5864..b1657b284 100644 --- a/third_party/azure.BUILD +++ b/third_party/azure.BUILD @@ -16,7 +16,6 @@ cc_library( ]), hdrs = [], defines = [ - "azure_storage_lite_EXPORTS", "USE_OPENSSL", ] + select({ "@bazel_tools//src/conditions:windows": [ From 5a42d1e5f2ad4c3b0bec945e017953f124e9147c Mon Sep 17 00:00:00 2001 From: Vignesh Kothapalli Date: Sun, 31 Jan 2021 09:08:29 +0530 Subject: [PATCH 47/85] Add reference to github-pages benchmarks in README (#1289) * add reference to github-pages benchmarks * minor grammar change * Update README.md Co-authored-by: Yuan Tang Co-authored-by: Yuan Tang --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index a24ef1b4e..2f84dc764 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,12 @@ of releases [here](https://github.com/tensorflow/io/releases). | 0.2.0 | 1.12.0 | Jan 29, 2019 | | 0.1.0 | 1.12.0 | Dec 16, 2018 | + +## Performance Benchmarking + +We use [github-pages](https://tensorflow.github.io/io/dev/bench/) to document the results of API performance benchmarks. The benchmark job is triggered on every commit to `master` branch and +facilitates tracking performance w.r.t commits. + ## Contributing Tensorflow I/O is a community led open source project. As such, the project From ff6245a24c6a0299f733949c948965a10b4670b4 Mon Sep 17 00:00:00 2001 From: Cheng Ren Date: Sat, 30 Jan 2021 23:28:07 -0800 Subject: [PATCH 48/85] Update _toc.yaml (#1290) --- docs/tutorials/_toc.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/tutorials/_toc.yaml b/docs/tutorials/_toc.yaml index 3ec2d2f26..1c2ee891d 100644 --- a/docs/tutorials/_toc.yaml +++ b/docs/tutorials/_toc.yaml @@ -34,3 +34,6 @@ toc: path: /io/tutorials/kafka - title: "Elasticsearch" path: /io/tutorials/elasticsearch +- title: "Avro" + path: /io/tutorials/avro + From 8a1feadf301e47fabb42b9ca415a0389bb74f02f Mon Sep 17 00:00:00 2001 From: Mark Daoust Date: Tue, 2 Feb 2021 07:13:25 -0800 Subject: [PATCH 49/85] Clear outputs (#1292) --- docs/tutorials/avro.ipynb | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/docs/tutorials/avro.ipynb b/docs/tutorials/avro.ipynb index f34c81503..324585af8 100644 --- a/docs/tutorials/avro.ipynb +++ b/docs/tutorials/avro.ipynb @@ -138,16 +138,7 @@ "metadata": { "id": "dX74RKfZ_TdF" }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "tensorflow-io version: 0.17.0\n", - "tensorflow version: 2.4.0\n" - ] - } - ], + "outputs": [], "source": [ "print(\"tensorflow-io version: {}\".format(tfio.__version__))\n", "print(\"tensorflow version: {}\".format(tf.__version__))" From b07d1ab8fbf8b88d6cc2a487078363059bc512aa Mon Sep 17 00:00:00 2001 From: Vignesh Kothapalli Date: Wed, 3 Feb 2021 01:00:47 +0530 Subject: [PATCH 50/85] fix kafka online-learning section in tutorial notebook (#1274) * kafka notebook fix for colab env * change timeout from 30 to 20 seconds * reduce stream_timeout --- docs/tutorials/kafka.ipynb | 60 +++++--------------------------------- 1 file changed, 8 insertions(+), 52 deletions(-) diff --git a/docs/tutorials/kafka.ipynb b/docs/tutorials/kafka.ipynb index 07d8b2ce2..a5af9decc 100644 --- a/docs/tutorials/kafka.ipynb +++ b/docs/tutorials/kafka.ipynb @@ -74,7 +74,7 @@ "\n", "Kafka is primarily a distributed event-streaming platform which provides scalable and fault-tolerant streaming data across data pipelines. It is an essential technical component of a plethora of major enterprises where mission-critical data delivery is a primary requirement.\n", "\n", - "**NOTE:** A basic understanding of the [kafka components](https://kafka.apache.org/documentation/#intro_concepts_and_terms) will help you in following the tutorial with ease.", + "**NOTE:** A basic understanding of the [kafka components](https://kafka.apache.org/documentation/#intro_concepts_and_terms) will help you in following the tutorial with ease.\n", "\n", "**NOTE:** A Java runtime environment is required to run this tutorial." ] @@ -755,7 +755,7 @@ "source": [ "### The tfio training dataset for online learning\n", "\n", - "The `streaming.KafkaBatchIODataset` is similar to the `streaming.KafkaGroupIODataset` in it's API. Additionally, it is recommended to utilize the `stream_timeout` parameter to configure the duration for which the dataset will block for new messages before timing out. In the instance below, the dataset is configured with a `stream_timeout` of `30000` milliseconds. This implies that, after all the messages from the topic have been consumed, the dataset will wait for an additional 30 seconds before timing out and disconnecting from the kafka cluster. If new messages are streamed into the topic before timing out, the data consumption and model training resumes for those newly consumed data points. To block indefinitely, set it to `-1`." + "The `streaming.KafkaBatchIODataset` is similar to the `streaming.KafkaGroupIODataset` in it's API. Additionally, it is recommended to utilize the `stream_timeout` parameter to configure the duration for which the dataset will block for new messages before timing out. In the instance below, the dataset is configured with a `stream_timeout` of `10000` milliseconds. This implies that, after all the messages from the topic have been consumed, the dataset will wait for an additional 10 seconds before timing out and disconnecting from the kafka cluster. If new messages are streamed into the topic before timing out, the data consumption and model training resumes for those newly consumed data points. To block indefinitely, set it to `-1`." ] }, { @@ -770,7 +770,7 @@ " topics=[\"susy-train\"],\n", " group_id=\"cgonline\",\n", " servers=\"127.0.0.1:9092\",\n", - " stream_timeout=30000, # in milliseconds, to block indefinitely, set it to -1.\n", + " stream_timeout=10000, # in milliseconds, to block indefinitely, set it to -1.\n", " configuration=[\n", " \"session.timeout.ms=7000\",\n", " \"max.poll.interval.ms=8000\",\n", @@ -779,50 +779,6 @@ ")" ] }, - { - "cell_type": "markdown", - "metadata": { - "id": "sJronJPnZhyR" - }, - "source": [ - "In addition to training the model on existing data, a background thread will be started, which will start streaming additional data into the `susy-train` topic after a sleep duration of 30 seconds. This demonstrates the functionality of resuming the training as soons as new data is fed into the topic without the need for building the dataset over and over again." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "iaBjhFkmZd1C" - }, - "outputs": [], - "source": [ - "def error_callback(exc):\n", - " raise Exception('Error while sendig data to kafka: {0}'.format(str(exc)))\n", - "\n", - "def write_to_kafka_after_sleep(topic_name, items):\n", - " time.sleep(30)\n", - " print(\"#\"*100)\n", - " print(\"Writing messages into topic: {0} after a nice sleep !\".format(topic_name))\n", - " print(\"#\"*100)\n", - " count=0\n", - " producer = KafkaProducer(bootstrap_servers=['127.0.0.1:9092'])\n", - " for message, key in items:\n", - " producer.send(topic_name,\n", - " key=key.encode('utf-8'),\n", - " value=message.encode('utf-8')\n", - " ).add_errback(error_callback)\n", - " count+=1\n", - " producer.flush()\n", - " print(\"#\"*100)\n", - " print(\"Wrote {0} messages into topic: {1}\".format(count, topic_name))\n", - " print(\"#\"*100)\n", - "\n", - "def decode_kafka_online_item(raw_message, raw_key):\n", - " message = tf.io.decode_csv(raw_message, [[0.0] for i in range(NUM_COLUMNS)])\n", - " key = tf.strings.to_number(raw_key)\n", - " return (message, key)\n" - ] - }, { "cell_type": "markdown", "metadata": { @@ -840,11 +796,11 @@ }, "outputs": [], "source": [ - "thread = threading.Thread(target=write_to_kafka_after_sleep,\n", - " args=(\"susy-train\", zip(x_train, y_train)))\n", - "thread.daemon = True\n", - "thread.start()\n", - "\n", + "def decode_kafka_online_item(raw_message, raw_key):\n", + " message = tf.io.decode_csv(raw_message, [[0.0] for i in range(NUM_COLUMNS)])\n", + " key = tf.strings.to_number(raw_key)\n", + " return (message, key)\n", + " \n", "for mini_ds in online_train_ds:\n", " mini_ds = mini_ds.shuffle(buffer_size=32)\n", " mini_ds = mini_ds.map(decode_kafka_online_item)\n", From 5299d147a4d7fc4c2283c943cc287bb9bcd8253a Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Tue, 2 Feb 2021 11:32:07 -0800 Subject: [PATCH 51/85] Only enable bazel caching writes for tensorflow/io github actions (#1293) This PR updates so that only GitHub actions run on tensorflow/io repo will be enabled with bazel cache writes. Without the updates, a focked repo actions will cause error. Note once bazel cache read-permissions are enabled from gcs forked repo will be able to access bazel cache (read-only). Signed-off-by: Yong Tang --- .github/workflows/build.yml | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8abdb9b5a..5f7ef6318 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,6 +10,7 @@ on: env: GCP_CREDS: ${{ secrets.GCP_CREDS }} + REPO_NAME: ${{ github.repository }} EVENT_NAME: ${{ github.event_name }} BAZEL_OPTIMIZATION: --copt=-msse4.2 --copt=-mavx --compilation_mode=opt @@ -51,7 +52,7 @@ jobs: steps: - uses: actions/checkout@v2 - run: | - if [[ "${EVENT_NAME}" == "push" ]]; then + if [[ "${EVENT_NAME}" == "push" && "${REPO_NAME}" == "tensorflow/io" ]]; then printf '%s\n' "${GCP_CREDS}" >service_account_creds.json export BAZEL_OPTIMIZATION="--remote_cache=https://storage.googleapis.com/tensorflow-sigs-io --remote_upload_local_results=true --google_credentials=service_account_creds.json" fi @@ -71,7 +72,7 @@ jobs: steps: - uses: actions/checkout@v2 - run: | - if [[ "${EVENT_NAME}" == "push" ]]; then + if [[ "${EVENT_NAME}" == "push" && "${REPO_NAME}" == "tensorflow/io" ]]; then printf '%s\n' "${GCP_CREDS}" >service_account_creds.json export BAZEL_OPTIMIZATION="--remote_cache=https://storage.googleapis.com/tensorflow-sigs-io --remote_upload_local_results=true --google_credentials=service_account_creds.json" fi @@ -89,7 +90,7 @@ jobs: steps: - uses: actions/checkout@v2 - run: | - if [[ "${EVENT_NAME}" == "push" ]]; then + if [[ "${EVENT_NAME}" == "push" && "${REPO_NAME}" == "tensorflow/io" ]]; then printf '%s\n' "${GCP_CREDS}" >service_account_creds.json export BAZEL_OPTIMIZATION="--remote_cache=https://storage.googleapis.com/tensorflow-sigs-io --remote_upload_local_results=true --google_credentials=service_account_creds.json" fi @@ -108,7 +109,7 @@ jobs: - uses: actions/checkout@v2 - name: Bazel on macOS run: | - if [[ "${EVENT_NAME}" == "push" ]]; then + if [[ "${EVENT_NAME}" == "push" && "${REPO_NAME}" == "tensorflow/io" ]]; then printf '%s\n' "${GCP_CREDS}" >service_account_creds.json export BAZEL_OPTIMIZATION="${BAZEL_OPTIMIZATION} --remote_cache=https://storage.googleapis.com/tensorflow-sigs-io --remote_upload_local_results=true --google_credentials=service_account_creds.json" fi @@ -219,7 +220,7 @@ jobs: - uses: actions/checkout@v2 - name: Bazel on Linux run: | - if [[ "${EVENT_NAME}" == "push" ]]; then + if [[ "${EVENT_NAME}" == "push" && "${REPO_NAME}" == "tensorflow/io" ]]; then printf '%s\n' "${GCP_CREDS}" >service_account_creds.json export BAZEL_OPTIMIZATION="${BAZEL_OPTIMIZATION} --remote_cache=https://storage.googleapis.com/tensorflow-sigs-io --remote_upload_local_results=true --google_credentials=service_account_creds.json" fi @@ -327,8 +328,12 @@ jobs: shell: cmd run: | if "%EVENT_NAME%" == "push" ( - printenv GCP_CREDS > service_account_creds.json - set "BAZEL_OPTIMIZATION=--remote_cache=https://storage.googleapis.com/tensorflow-sigs-io --remote_upload_local_results=true --google_credentials=service_account_creds.json" + if "%REPO_NAME%" == "tensorflow/io" ( + printenv GCP_CREDS > service_account_creds.json + set "BAZEL_OPTIMIZATION=--remote_cache=https://storage.googleapis.com/tensorflow-sigs-io --remote_upload_local_results=true --google_credentials=service_account_creds.json" + ) else ( + echo %REPO_NAME% + ) ) else ( echo %EVENT_NAME% ) From f02af152c0a8d1969c16285223403fdc7887a06f Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Tue, 2 Feb 2021 15:04:58 -0800 Subject: [PATCH 52/85] Enable ready-only bazel cache (#1294) This PR enables read-only bazel cache Signed-off-by: Yong Tang --- .github/workflows/build.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5f7ef6318..0df1e111d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -55,6 +55,8 @@ jobs: if [[ "${EVENT_NAME}" == "push" && "${REPO_NAME}" == "tensorflow/io" ]]; then printf '%s\n' "${GCP_CREDS}" >service_account_creds.json export BAZEL_OPTIMIZATION="--remote_cache=https://storage.googleapis.com/tensorflow-sigs-io --remote_upload_local_results=true --google_credentials=service_account_creds.json" + else + export BAZEL_OPTIMIZATION="--remote_cache=https://storage.googleapis.com/tensorflow-sigs-io --remote_upload_local_results=false" fi set -x -e echo "Bring /usr/bin to front as GitHub does not use system python3 by default" @@ -75,6 +77,8 @@ jobs: if [[ "${EVENT_NAME}" == "push" && "${REPO_NAME}" == "tensorflow/io" ]]; then printf '%s\n' "${GCP_CREDS}" >service_account_creds.json export BAZEL_OPTIMIZATION="--remote_cache=https://storage.googleapis.com/tensorflow-sigs-io --remote_upload_local_results=true --google_credentials=service_account_creds.json" + else + export BAZEL_OPTIMIZATION="--remote_cache=https://storage.googleapis.com/tensorflow-sigs-io --remote_upload_local_results=false" fi set -x -e bash -x -e .github/workflows/build.space.sh @@ -93,6 +97,8 @@ jobs: if [[ "${EVENT_NAME}" == "push" && "${REPO_NAME}" == "tensorflow/io" ]]; then printf '%s\n' "${GCP_CREDS}" >service_account_creds.json export BAZEL_OPTIMIZATION="--remote_cache=https://storage.googleapis.com/tensorflow-sigs-io --remote_upload_local_results=true --google_credentials=service_account_creds.json" + else + export BAZEL_OPTIMIZATION="--remote_cache=https://storage.googleapis.com/tensorflow-sigs-io --remote_upload_local_results=false" fi set -x -e bash -x -e .github/workflows/build.space.sh @@ -112,6 +118,8 @@ jobs: if [[ "${EVENT_NAME}" == "push" && "${REPO_NAME}" == "tensorflow/io" ]]; then printf '%s\n' "${GCP_CREDS}" >service_account_creds.json export BAZEL_OPTIMIZATION="${BAZEL_OPTIMIZATION} --remote_cache=https://storage.googleapis.com/tensorflow-sigs-io --remote_upload_local_results=true --google_credentials=service_account_creds.json" + else + export BAZEL_OPTIMIZATION="${BAZEL_OPTIMIZATION} --remote_cache=https://storage.googleapis.com/tensorflow-sigs-io --remote_upload_local_results=false" fi set -x -e echo "Bring /usr/bin to front as GitHub does not use system python3 by default" @@ -223,6 +231,8 @@ jobs: if [[ "${EVENT_NAME}" == "push" && "${REPO_NAME}" == "tensorflow/io" ]]; then printf '%s\n' "${GCP_CREDS}" >service_account_creds.json export BAZEL_OPTIMIZATION="${BAZEL_OPTIMIZATION} --remote_cache=https://storage.googleapis.com/tensorflow-sigs-io --remote_upload_local_results=true --google_credentials=service_account_creds.json" + else + export BAZEL_OPTIMIZATION="${BAZEL_OPTIMIZATION} --remote_cache=https://storage.googleapis.com/tensorflow-sigs-io --remote_upload_local_results=false" fi set -x -e bash -x -e .github/workflows/build.space.sh @@ -333,9 +343,11 @@ jobs: set "BAZEL_OPTIMIZATION=--remote_cache=https://storage.googleapis.com/tensorflow-sigs-io --remote_upload_local_results=true --google_credentials=service_account_creds.json" ) else ( echo %REPO_NAME% + set "BAZEL_OPTIMIZATION=--remote_cache=https://storage.googleapis.com/tensorflow-sigs-io --remote_upload_local_results=false" ) ) else ( echo %EVENT_NAME% + set "BAZEL_OPTIMIZATION=--remote_cache=https://storage.googleapis.com/tensorflow-sigs-io --remote_upload_local_results=false" ) @echo on set /P BAZEL_VERSION=< .bazelversion From 84bba4ceece6a28482982f5bfdf3574b9576dcbc Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Tue, 2 Feb 2021 18:40:41 -0800 Subject: [PATCH 53/85] Update xz to 5.2.5, and switch the download link. (#1296) This PR updates xz to 5.2.5, and switch the download link to use github instead as it is more stable. Signed-off-by: Yong Tang --- WORKSPACE | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/WORKSPACE b/WORKSPACE index 69b5710d1..3475df9e7 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -440,11 +440,11 @@ http_archive( http_archive( name = "xz", build_file = "//third_party:xz.BUILD", - sha256 = "b512f3b726d3b37b6dc4c8570e137b9311e7552e8ccbab4d39d47ce5f4177145", - strip_prefix = "xz-5.2.4", + sha256 = "0d2b89629f13dd1a0602810529327195eff5f62a0142ccd65b903bc16a4ac78a", + strip_prefix = "xz-5.2.5", urls = [ - "https://storage.googleapis.com/mirror.tensorflow.org/tukaani.org/xz/xz-5.2.4.tar.gz", - "https://tukaani.org/xz/xz-5.2.4.tar.gz", + "https://storage.googleapis.com/mirror.tensorflow.org/github.com/xz-mirror/xz/archive/v5.2.5.tar.gz", + "https://github.com/xz-mirror/xz/archive/v5.2.5.tar.gz", ], ) From 0a7c5a250bdaaccdac098f63aa6de7beaf17ff5a Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Tue, 2 Feb 2021 19:05:37 -0800 Subject: [PATCH 54/85] Enable bazel remote cache for kokoro tests (#1295) --- .kokorun/io_cpu.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.kokorun/io_cpu.sh b/.kokorun/io_cpu.sh index 3539922e8..ab4a43480 100755 --- a/.kokorun/io_cpu.sh +++ b/.kokorun/io_cpu.sh @@ -48,7 +48,7 @@ docker --version export PYTHON_VERSION=3.8 export BAZEL_VERSION=$(cat .bazelversion) -export BAZEL_OPTIMIZATION="--copt=-msse4.2 --copt=-mavx --compilation_mode=opt" +export BAZEL_OPTIMIZATION="--copt=-msse4.2 --copt=-mavx --compilation_mode=opt --remote_cache=https://storage.googleapis.com/tensorflow-sigs-io --remote_upload_local_results=false" export BAZEL_OS=$(uname | tr '[:upper:]' '[:lower:]') docker run -i --rm -v $PWD:/v -w /v --net=host \ From 880c8b337f3ac653c37261a04daf261567534b67 Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Wed, 3 Feb 2021 13:18:11 -0800 Subject: [PATCH 55/85] Rename tests (#1297) --- .github/workflows/build.wheel.sh | 6 +- .github/workflows/build.yml | 13 +- docs/development.md | 12 +- ...{test_archive_eager.py => test_archive.py} | 0 tests/{test_arrow_eager.py => test_arrow.py} | 0 tests/{test_audio_eager.py => test_audio.py} | 0 ...t_audio_ops_eager.py => test_audio_ops.py} | 0 tests/{test_avro_eager.py => test_avro.py} | 0 ...est_bigquery_eager.py => test_bigquery.py} | 0 ...est_bigtable_eager.py => test_bigtable.py} | 0 tests/{test_color_eager.py => test_color.py} | 0 tests/{test_csv_eager.py => test_csv.py} | 0 tests/test_dicom.py | 135 ++- tests/test_dicom_eager.py | 223 ---- ...ntation_eager.py => test_documentation.py} | 0 ...csearch_eager.py => test_elasticsearch.py} | 0 ...{test_feather_eager.py => test_feather.py} | 0 .../{test_ffmpeg_eager.py => test_ffmpeg.py} | 0 .../{test_filter_eager.py => test_filter.py} | 0 tests/{test_gcs_eager.py => test_gcs.py} | 0 tests/test_genome.py | 46 +- ...test_genome_eager.py => test_genome_v1.py} | 46 +- tests/{test_hdf5_eager.py => test_hdf5.py} | 0 tests/{test_hdfs_eager.py => test_hdfs.py} | 0 tests/{test_http_eager.py => test_http.py} | 0 tests/{test_ignite.py => test_ignite_v1.py} | 0 tests/{test_image_eager.py => test_image.py} | 0 ...io_dataset_eager.py => test_io_dataset.py} | 0 ...est_io_layer_eager.py => test_io_layer.py} | 0 ...t_io_tensor_eager.py => test_io_tensor.py} | 0 tests/{test_json_eager.py => test_json.py} | 0 tests/test_kafka.py | 974 +++++++++--------- tests/test_kafka_eager.py | 515 --------- tests/test_kafka_v1.py | 511 +++++++++ .../{test_libsvm_eager.py => test_libsvm.py} | 0 tests/{test_lmdb_eager.py => test_lmdb.py} | 0 ...{test_mongodb_eager.py => test_mongodb.py} | 0 ...{test_parquet_eager.py => test_parquet.py} | 0 ...parse_avro_eager.py => test_parse_avro.py} | 0 tests/{test_pcap_eager.py => test_pcap.py} | 0 .../{test_pulsar_eager.py => test_pulsar.py} | 0 tests/{test_s3_eager.py => test_s3.py} | 0 ...ization_eager.py => test_serialization.py} | 0 tests/{test_text_eager.py => test_text.py} | 0 ...{test_version_eager.py => test_version.py} | 0 tests/{test_video_eager.py => test_video.py} | 0 46 files changed, 1168 insertions(+), 1313 deletions(-) rename tests/{test_archive_eager.py => test_archive.py} (100%) rename tests/{test_arrow_eager.py => test_arrow.py} (100%) rename tests/{test_audio_eager.py => test_audio.py} (100%) rename tests/{test_audio_ops_eager.py => test_audio_ops.py} (100%) rename tests/{test_avro_eager.py => test_avro.py} (100%) rename tests/{test_bigquery_eager.py => test_bigquery.py} (100%) rename tests/{test_bigtable_eager.py => test_bigtable.py} (100%) rename tests/{test_color_eager.py => test_color.py} (100%) rename tests/{test_csv_eager.py => test_csv.py} (100%) delete mode 100644 tests/test_dicom_eager.py rename tests/{test_documentation_eager.py => test_documentation.py} (100%) rename tests/{test_elasticsearch_eager.py => test_elasticsearch.py} (100%) rename tests/{test_feather_eager.py => test_feather.py} (100%) rename tests/{test_ffmpeg_eager.py => test_ffmpeg.py} (100%) rename tests/{test_filter_eager.py => test_filter.py} (100%) rename tests/{test_gcs_eager.py => test_gcs.py} (100%) rename tests/{test_genome_eager.py => test_genome_v1.py} (79%) rename tests/{test_hdf5_eager.py => test_hdf5.py} (100%) rename tests/{test_hdfs_eager.py => test_hdfs.py} (100%) rename tests/{test_http_eager.py => test_http.py} (100%) rename tests/{test_ignite.py => test_ignite_v1.py} (100%) rename tests/{test_image_eager.py => test_image.py} (100%) rename tests/{test_io_dataset_eager.py => test_io_dataset.py} (100%) rename tests/{test_io_layer_eager.py => test_io_layer.py} (100%) rename tests/{test_io_tensor_eager.py => test_io_tensor.py} (100%) rename tests/{test_json_eager.py => test_json.py} (100%) delete mode 100644 tests/test_kafka_eager.py create mode 100644 tests/test_kafka_v1.py rename tests/{test_libsvm_eager.py => test_libsvm.py} (100%) rename tests/{test_lmdb_eager.py => test_lmdb.py} (100%) rename tests/{test_mongodb_eager.py => test_mongodb.py} (100%) rename tests/{test_parquet_eager.py => test_parquet.py} (100%) rename tests/{test_parse_avro_eager.py => test_parse_avro.py} (100%) rename tests/{test_pcap_eager.py => test_pcap.py} (100%) rename tests/{test_pulsar_eager.py => test_pulsar.py} (100%) rename tests/{test_s3_eager.py => test_s3.py} (100%) rename tests/{test_serialization_eager.py => test_serialization.py} (100%) rename tests/{test_text_eager.py => test_text.py} (100%) rename tests/{test_version_eager.py => test_version.py} (100%) rename tests/{test_video_eager.py => test_video.py} (100%) diff --git a/.github/workflows/build.wheel.sh b/.github/workflows/build.wheel.sh index 502984e72..afb382cbb 100755 --- a/.github/workflows/build.wheel.sh +++ b/.github/workflows/build.wheel.sh @@ -7,10 +7,10 @@ run_test() { CPYTHON_VERSION=$($entry -c 'import sys; print(str(sys.version_info[0])+str(sys.version_info[1]))') (cd wheelhouse && $entry -m pip install tensorflow_io-*-cp${CPYTHON_VERSION}-*.whl) $entry -m pip install -q pytest pytest-benchmark boto3 fastavro avro-python3 scikit-image pandas pyarrow==3.0.0 google-cloud-pubsub==2.1.0 google-cloud-bigtable==1.6.0 google-cloud-bigquery-storage==1.1.0 google-cloud-bigquery==2.3.1 google-cloud-storage==1.32.0 - (cd tests && $entry -m pytest --benchmark-disable -v --import-mode=append $(find . -type f \( -iname "test_*.py" ! \( -iname "test_*_eager.py" \) \))) - (cd tests && $entry -m pytest --benchmark-disable -v --import-mode=append $(find . -type f \( -iname "test_*_eager.py" ! \( -iname "test_bigquery_eager.py" \) \))) + (cd tests && $entry -m pytest --benchmark-disable -v --import-mode=append $(find . -type f \( -iname "test_*_v1.py" \))) + (cd tests && $entry -m pytest --benchmark-disable -v --import-mode=append $(find . -type f \( -iname "test_*.py" ! \( -iname "test_*_v1.py" -o -iname "test_bigquery.py" \) \))) # GRPC and test_bigquery_eager tests have to be executed separately because of https://github.com/grpc/grpc/issues/20034 - (cd tests && $entry -m pytest --benchmark-disable -v --import-mode=append $(find . -type f \( -iname "test_bigquery_eager.py" \))) + (cd tests && $entry -m pytest --benchmark-disable -v --import-mode=append $(find . -type f \( -iname "test_bigquery.py" \))) } PYTHON_VERSION=python diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0df1e111d..74128276b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -434,14 +434,13 @@ jobs: python --version python -m pip install -U pytest-benchmark rm -rf tensorflow_io - (cd tests && python -m pytest -s -v test_lmdb_eager.py) - (python -m pytest -s -v test_image_eager.py -k "webp or ppm or bmp or bounding or exif or hdr or openexr or tiff or avif") - (python -m pytest -s -v test_serialization_eager.py) - (python -m pytest -s -v test_io_dataset_eager.py -k "numpy or hdf5 or audio or to_file") - (python -m pytest -s -v test_http_eager.py) + (cd tests && python -m pytest -s -v test_lmdb.py) + (python -m pytest -s -v test_image.py -k "webp or ppm or bmp or bounding or exif or hdr or openexr or tiff or avif") + (python -m pytest -s -v test_serialization.py) + (python -m pytest -s -v test_io_dataset.py -k "numpy or hdf5 or audio or to_file") + (python -m pytest -s -v test_http.py) python -m pip install google-cloud-bigquery-storage==0.7.0 google-cloud-bigquery==1.22.0 fastavro - (python -m pytest -s -v test_bigquery_eager.py) - (python -m pytest -s -v test_dicom_eager.py) + (python -m pytest -s -v test_bigquery.py) (python -m pytest -s -v test_dicom.py) release: diff --git a/docs/development.md b/docs/development.md index c23c7d66a..5c43a509a 100644 --- a/docs/development.md +++ b/docs/development.md @@ -88,7 +88,7 @@ bazel build -s --verbose_failures $BAZEL_OPTIMIZATION //tensorflow_io/... # `bazel-bin/tensorflow_io/core/python/ops/` and it is possible # to run tests with `pytest`, e.g.: sudo python3 -m pip install pytest -TFIO_DATAPATH=bazel-bin python3 -m pytest -s -v tests/test_serialization_eager.py +TFIO_DATAPATH=bazel-bin python3 -m pytest -s -v tests/test_serialization.py ``` NOTE: When running pytest, `TFIO_DATAPATH=bazel-bin` has to be passed so that python can utilize the generated shared libraries after the build process. @@ -147,7 +147,7 @@ bazel build -s --verbose_failures $BAZEL_OPTIMIZATION //tensorflow_io/... # `bazel-bin/tensorflow_io/core/python/ops/` and it is possible # to run tests with `pytest`, e.g.: sudo python3 -m pip install pytest -TFIO_DATAPATH=bazel-bin python3 -m pytest -s -v tests/test_serialization_eager.py +TFIO_DATAPATH=bazel-bin python3 -m pytest -s -v tests/test_serialization.py ``` ##### CentOS 8 @@ -207,7 +207,7 @@ scl enable rh-python36 devtoolset-9 \ TFIO_DATAPATH=bazel-bin \ scl enable rh-python36 devtoolset-9 \ - 'python3 -m pytest -s -v tests/test_serialization_eager.py' + 'python3 -m pytest -s -v tests/test_serialization.py' ``` #### Python Wheels @@ -295,7 +295,7 @@ use: $ bash -x -e tests/test_kafka/kafka_test.sh # Run the tests -$ TFIO_DATAPATH=bazel-bin pytest -s -vv tests/test_kafka_eager.py +$ TFIO_DATAPATH=bazel-bin pytest -s -vv tests/test_kafka.py ``` Testing `Datasets` associated with tools such as `Elasticsearch` or `MongoDB` @@ -307,7 +307,7 @@ require docker to be available on the system. In such scenarios, use: $ bash tests/test_elasticsearch/elasticsearch_test.sh start # Run the tests -$ TFIO_DATAPATH=bazel-bin pytest -s -vv tests/test_elasticsearch_eager.py +$ TFIO_DATAPATH=bazel-bin pytest -s -vv tests/test_elasticsearch.py # Stop and remove the container $ bash tests/test_elasticsearch/elasticsearch_test.sh stop @@ -319,7 +319,7 @@ For example, to run tests related to `parquet` dataset's, use: ```sh # Just run the test -$ TFIO_DATAPATH=bazel-bin pytest -s -vv tests/test_parquet_eager.py +$ TFIO_DATAPATH=bazel-bin pytest -s -vv tests/test_parquet.py ``` diff --git a/tests/test_archive_eager.py b/tests/test_archive.py similarity index 100% rename from tests/test_archive_eager.py rename to tests/test_archive.py diff --git a/tests/test_arrow_eager.py b/tests/test_arrow.py similarity index 100% rename from tests/test_arrow_eager.py rename to tests/test_arrow.py diff --git a/tests/test_audio_eager.py b/tests/test_audio.py similarity index 100% rename from tests/test_audio_eager.py rename to tests/test_audio.py diff --git a/tests/test_audio_ops_eager.py b/tests/test_audio_ops.py similarity index 100% rename from tests/test_audio_ops_eager.py rename to tests/test_audio_ops.py diff --git a/tests/test_avro_eager.py b/tests/test_avro.py similarity index 100% rename from tests/test_avro_eager.py rename to tests/test_avro.py diff --git a/tests/test_bigquery_eager.py b/tests/test_bigquery.py similarity index 100% rename from tests/test_bigquery_eager.py rename to tests/test_bigquery.py diff --git a/tests/test_bigtable_eager.py b/tests/test_bigtable.py similarity index 100% rename from tests/test_bigtable_eager.py rename to tests/test_bigtable.py diff --git a/tests/test_color_eager.py b/tests/test_color.py similarity index 100% rename from tests/test_color_eager.py rename to tests/test_color.py diff --git a/tests/test_csv_eager.py b/tests/test_csv.py similarity index 100% rename from tests/test_csv_eager.py rename to tests/test_csv.py diff --git a/tests/test_dicom.py b/tests/test_dicom.py index c4605e61a..e867465cb 100644 --- a/tests/test_dicom.py +++ b/tests/test_dicom.py @@ -16,6 +16,7 @@ import os +import numpy as np import pytest import tensorflow as tf @@ -35,8 +36,7 @@ def test_dicom_input(): - """test_dicom_input - """ + """test_dicom_input""" _ = tfio.image.decode_dicom_data _ = tfio.image.decode_dicom_image _ = tfio.image.dicom_tags @@ -66,32 +66,26 @@ def test_dicom_input(): ("MR-MONO2-12-shoulder.dcm", (1, 1024, 1024, 1)), ("OT-MONO2-8-a7.dcm", (1, 512, 512, 1)), ("US-PAL-8-10x-echo.dcm", (10, 430, 600, 3)), + ("TOSHIBA_J2K_OpenJPEGv2Regression.dcm", (1, 512, 512, 1)), ], ) def test_decode_dicom_image(fname, exp_shape): - """test_decode_dicom_image - """ + """test_decode_dicom_image""" dcm_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), "test_dicom", fname ) - g1 = tf.compat.v1.Graph() + file_contents = tf.io.read_file(filename=dcm_path) - with g1.as_default(): - file_contents = tf.io.read_file(filename=dcm_path) - dcm_image = tfio.image.decode_dicom_image( - contents=file_contents, - dtype=tf.float32, - on_error="strict", - scale="auto", - color_dim=True, - ) - - sess = tf.compat.v1.Session(graph=g1) - dcm_image_np = sess.run(dcm_image) - - assert dcm_image_np.shape == exp_shape + dcm_image = tfio.image.decode_dicom_image( + contents=file_contents, + dtype=tf.float32, + on_error="strict", + scale="auto", + color_dim=True, + ) + assert dcm_image.numpy().shape == exp_shape @pytest.mark.parametrize( @@ -121,23 +115,108 @@ def test_decode_dicom_image(fname, exp_shape): ], ) def test_decode_dicom_data(fname, tag, exp_value): - """test_decode_dicom_data - """ + """test_decode_dicom_data""" dcm_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), "test_dicom", fname ) - g1 = tf.compat.v1.Graph() + file_contents = tf.io.read_file(filename=dcm_path) + + dcm_data = tfio.image.decode_dicom_data(contents=file_contents, tags=tag) + + assert dcm_data.numpy() == exp_value + + +def test_dicom_image_shape(): + """test_decode_dicom_image""" + + dcm_path = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "test_dicom", + "US-PAL-8-10x-echo.dcm", + ) + + dataset = tf.data.Dataset.from_tensor_slices([dcm_path]) + dataset = dataset.map(tf.io.read_file) + dataset = dataset.map(lambda e: tfio.image.decode_dicom_image(e, dtype=tf.uint16)) + dataset = dataset.map(lambda e: tf.image.resize(e, (224, 224))) + + +def test_dicom_image_concurrency(): + """test_decode_dicom_image_currency""" - with g1.as_default(): - file_contents = tf.io.read_file(filename=dcm_path) - dcm_data = tfio.image.decode_dicom_data(contents=file_contents, tags=tag) + @tf.function + def preprocess(dcm_content): + tags = tfio.image.decode_dicom_data( + dcm_content, tags=[tfio.image.dicom_tags.PatientsName] + ) + tf.print(tags) + image = tfio.image.decode_dicom_image(dcm_content, dtype=tf.float32) + return image + + dcm_path = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "test_dicom", + "TOSHIBA_J2K_OpenJPEGv2Regression.dcm", + ) + + dataset = ( + tf.data.Dataset.from_tensor_slices([dcm_path]) + .repeat() + .map(tf.io.read_file) + .map(preprocess, num_parallel_calls=8) + .take(200) + ) + for i, item in enumerate(dataset): + print(tf.shape(item), i) + assert np.array_equal(tf.shape(item), [1, 512, 512, 1]) + + dcm_path = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "test_dicom", + "US-PAL-8-10x-echo.dcm", + ) + + dataset = ( + tf.data.Dataset.from_tensor_slices([dcm_path]) + .repeat() + .map(tf.io.read_file) + .map(preprocess, num_parallel_calls=8) + .take(200) + ) + for i, item in enumerate(dataset): + print(tf.shape(item), i) + assert np.array_equal(tf.shape(item), [10, 430, 600, 3]) + + +def test_dicom_sequence(): + """test_decode_dicom_sequence""" + + dcm_path = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "test_dicom", + "2.25.304589190180579357564631626197663875025.dcm", + ) + dcm_content = tf.io.read_file(filename=dcm_path) + + tags = tfio.image.decode_dicom_data( + dcm_content, tags=["[0x0008,0x1115][0][0x0008,0x1140][0][0x0008,0x1155]"] + ) + assert np.array_equal(tags, [b"2.25.211904290918469145111906856660599393535"]) + + dcm_path = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "test_dicom", + "US-PAL-8-10x-echo.dcm", + ) + dcm_content = tf.io.read_file(filename=dcm_path) - sess = tf.compat.v1.Session(graph=g1) - dcm_data_np = sess.run(dcm_data) + tags = tfio.image.decode_dicom_data(dcm_content, tags=["[0x0020,0x000E]"]) + assert np.array_equal(tags, [b"999.999.94827453"]) - assert dcm_data_np == exp_value + tags = tfio.image.decode_dicom_data(dcm_content, tags=["0x0020,0x000e"]) + assert np.array_equal(tags, [b"999.999.94827453"]) if __name__ == "__main__": diff --git a/tests/test_dicom_eager.py b/tests/test_dicom_eager.py deleted file mode 100644 index e867465cb..000000000 --- a/tests/test_dicom_eager.py +++ /dev/null @@ -1,223 +0,0 @@ -# Copyright 2018 The TensorFlow Authors. All Rights Reserved. -# -# 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 DICOM.""" - - -import os -import numpy as np -import pytest - -import tensorflow as tf -import tensorflow_io as tfio - -# The DICOM sample files must be downloaded befor running the tests -# -# To download the DICOM samples: -# $ bash dicom_samples.sh download -# $ bash dicom_samples.sh extract -# -# To remopve the DICOM samples: -# $ bash dicom_samples.sh clean_dcm -# -# To remopve all the downloaded files: -# $ bash dicom_samples.sh clean_all - - -def test_dicom_input(): - """test_dicom_input""" - _ = tfio.image.decode_dicom_data - _ = tfio.image.decode_dicom_image - _ = tfio.image.dicom_tags - - -@pytest.mark.parametrize( - "fname, exp_shape", - [ - ("OT-MONO2-8-colon.dcm", (1, 512, 512, 1)), - ("CR-MONO1-10-chest.dcm", (1, 440, 440, 1)), - ("CT-MONO2-16-ort.dcm", (1, 512, 512, 1)), - ("MR-MONO2-16-head.dcm", (1, 256, 256, 1)), - ("US-RGB-8-epicard.dcm", (1, 480, 640, 3)), - ("CT-MONO2-8-abdo.dcm", (1, 512, 512, 1)), - ("MR-MONO2-16-knee.dcm", (1, 256, 256, 1)), - ("OT-MONO2-8-hip.dcm", (1, 512, 512, 1)), - ("US-RGB-8-esopecho.dcm", (1, 120, 256, 3)), - ("CT-MONO2-16-ankle.dcm", (1, 512, 512, 1)), - ("MR-MONO2-12-an2.dcm", (1, 256, 256, 1)), - ("MR-MONO2-8-16x-heart.dcm", (16, 256, 256, 1)), - ("OT-PAL-8-face.dcm", (1, 480, 640, 3)), - ("XA-MONO2-8-12x-catheter.dcm", (12, 512, 512, 1)), - ("CT-MONO2-16-brain.dcm", (1, 512, 512, 1)), - ("NM-MONO2-16-13x-heart.dcm", (13, 64, 64, 1)), - ("US-MONO2-8-8x-execho.dcm", (8, 120, 128, 1)), - ("CT-MONO2-16-chest.dcm", (1, 400, 512, 1)), - ("MR-MONO2-12-shoulder.dcm", (1, 1024, 1024, 1)), - ("OT-MONO2-8-a7.dcm", (1, 512, 512, 1)), - ("US-PAL-8-10x-echo.dcm", (10, 430, 600, 3)), - ("TOSHIBA_J2K_OpenJPEGv2Regression.dcm", (1, 512, 512, 1)), - ], -) -def test_decode_dicom_image(fname, exp_shape): - """test_decode_dicom_image""" - - dcm_path = os.path.join( - os.path.dirname(os.path.abspath(__file__)), "test_dicom", fname - ) - - file_contents = tf.io.read_file(filename=dcm_path) - - dcm_image = tfio.image.decode_dicom_image( - contents=file_contents, - dtype=tf.float32, - on_error="strict", - scale="auto", - color_dim=True, - ) - assert dcm_image.numpy().shape == exp_shape - - -@pytest.mark.parametrize( - "fname, tag, exp_value", - [ - ( - "OT-MONO2-8-colon.dcm", - tfio.image.dicom_tags.StudyInstanceUID, - b"1.3.46.670589.17.1.7.1.1.16", - ), - ("OT-MONO2-8-colon.dcm", tfio.image.dicom_tags.Rows, b"512"), - ("OT-MONO2-8-colon.dcm", tfio.image.dicom_tags.Columns, b"512"), - ("OT-MONO2-8-colon.dcm", tfio.image.dicom_tags.SamplesperPixel, b"1"), - ( - "US-PAL-8-10x-echo.dcm", - tfio.image.dicom_tags.StudyInstanceUID, - b"999.999.3859744", - ), - ( - "US-PAL-8-10x-echo.dcm", - tfio.image.dicom_tags.SeriesInstanceUID, - b"999.999.94827453", - ), - ("US-PAL-8-10x-echo.dcm", tfio.image.dicom_tags.NumberofFrames, b"10"), - ("US-PAL-8-10x-echo.dcm", tfio.image.dicom_tags.Rows, b"430"), - ("US-PAL-8-10x-echo.dcm", tfio.image.dicom_tags.Columns, b"600"), - ], -) -def test_decode_dicom_data(fname, tag, exp_value): - """test_decode_dicom_data""" - - dcm_path = os.path.join( - os.path.dirname(os.path.abspath(__file__)), "test_dicom", fname - ) - - file_contents = tf.io.read_file(filename=dcm_path) - - dcm_data = tfio.image.decode_dicom_data(contents=file_contents, tags=tag) - - assert dcm_data.numpy() == exp_value - - -def test_dicom_image_shape(): - """test_decode_dicom_image""" - - dcm_path = os.path.join( - os.path.dirname(os.path.abspath(__file__)), - "test_dicom", - "US-PAL-8-10x-echo.dcm", - ) - - dataset = tf.data.Dataset.from_tensor_slices([dcm_path]) - dataset = dataset.map(tf.io.read_file) - dataset = dataset.map(lambda e: tfio.image.decode_dicom_image(e, dtype=tf.uint16)) - dataset = dataset.map(lambda e: tf.image.resize(e, (224, 224))) - - -def test_dicom_image_concurrency(): - """test_decode_dicom_image_currency""" - - @tf.function - def preprocess(dcm_content): - tags = tfio.image.decode_dicom_data( - dcm_content, tags=[tfio.image.dicom_tags.PatientsName] - ) - tf.print(tags) - image = tfio.image.decode_dicom_image(dcm_content, dtype=tf.float32) - return image - - dcm_path = os.path.join( - os.path.dirname(os.path.abspath(__file__)), - "test_dicom", - "TOSHIBA_J2K_OpenJPEGv2Regression.dcm", - ) - - dataset = ( - tf.data.Dataset.from_tensor_slices([dcm_path]) - .repeat() - .map(tf.io.read_file) - .map(preprocess, num_parallel_calls=8) - .take(200) - ) - for i, item in enumerate(dataset): - print(tf.shape(item), i) - assert np.array_equal(tf.shape(item), [1, 512, 512, 1]) - - dcm_path = os.path.join( - os.path.dirname(os.path.abspath(__file__)), - "test_dicom", - "US-PAL-8-10x-echo.dcm", - ) - - dataset = ( - tf.data.Dataset.from_tensor_slices([dcm_path]) - .repeat() - .map(tf.io.read_file) - .map(preprocess, num_parallel_calls=8) - .take(200) - ) - for i, item in enumerate(dataset): - print(tf.shape(item), i) - assert np.array_equal(tf.shape(item), [10, 430, 600, 3]) - - -def test_dicom_sequence(): - """test_decode_dicom_sequence""" - - dcm_path = os.path.join( - os.path.dirname(os.path.abspath(__file__)), - "test_dicom", - "2.25.304589190180579357564631626197663875025.dcm", - ) - dcm_content = tf.io.read_file(filename=dcm_path) - - tags = tfio.image.decode_dicom_data( - dcm_content, tags=["[0x0008,0x1115][0][0x0008,0x1140][0][0x0008,0x1155]"] - ) - assert np.array_equal(tags, [b"2.25.211904290918469145111906856660599393535"]) - - dcm_path = os.path.join( - os.path.dirname(os.path.abspath(__file__)), - "test_dicom", - "US-PAL-8-10x-echo.dcm", - ) - dcm_content = tf.io.read_file(filename=dcm_path) - - tags = tfio.image.decode_dicom_data(dcm_content, tags=["[0x0020,0x000E]"]) - assert np.array_equal(tags, [b"999.999.94827453"]) - - tags = tfio.image.decode_dicom_data(dcm_content, tags=["0x0020,0x000e"]) - assert np.array_equal(tags, [b"999.999.94827453"]) - - -if __name__ == "__main__": - test.main() diff --git a/tests/test_documentation_eager.py b/tests/test_documentation.py similarity index 100% rename from tests/test_documentation_eager.py rename to tests/test_documentation.py diff --git a/tests/test_elasticsearch_eager.py b/tests/test_elasticsearch.py similarity index 100% rename from tests/test_elasticsearch_eager.py rename to tests/test_elasticsearch.py diff --git a/tests/test_feather_eager.py b/tests/test_feather.py similarity index 100% rename from tests/test_feather_eager.py rename to tests/test_feather.py diff --git a/tests/test_ffmpeg_eager.py b/tests/test_ffmpeg.py similarity index 100% rename from tests/test_ffmpeg_eager.py rename to tests/test_ffmpeg.py diff --git a/tests/test_filter_eager.py b/tests/test_filter.py similarity index 100% rename from tests/test_filter_eager.py rename to tests/test_filter.py diff --git a/tests/test_gcs_eager.py b/tests/test_gcs.py similarity index 100% rename from tests/test_gcs_eager.py rename to tests/test_gcs.py diff --git a/tests/test_genome.py b/tests/test_genome.py index 48b2642b3..1798b8914 100644 --- a/tests/test_genome.py +++ b/tests/test_genome.py @@ -19,8 +19,6 @@ import numpy as np import tensorflow as tf - -tf.compat.v1.disable_eager_execution() import tensorflow_io as tfio # pylint: disable=wrong-import-position fastq_path = os.path.join( @@ -30,13 +28,8 @@ def test_genome_fastq_reader(): """test_genome_fastq_reader""" - g1 = tf.compat.v1.Graph() - - with g1.as_default(): - data = tfio.genome.read_fastq(filename=fastq_path) - sess = tf.compat.v1.Session(graph=g1) - data_np = sess.run(data) + data = tfio.genome.read_fastq(filename=fastq_path) data_expected = [ b"GATTACA", @@ -52,8 +45,8 @@ def test_genome_fastq_reader(): b"FAD", ] - assert np.all(data_np.sequences == data_expected) - assert np.all(data_np.raw_quality == quality_expected) + assert np.all(data.sequences == data_expected) + assert np.all(data.raw_quality == quality_expected) def test_genome_sequences_to_onehot(): @@ -189,12 +182,10 @@ def test_genome_sequences_to_onehot(): [[0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 1, 0]], ] - with tf.compat.v1.Session() as sess: - raw_data = tfio.genome.read_fastq(filename=fastq_path) - data = tfio.genome.sequences_to_onehot(sequences=raw_data.sequences) - out = sess.run(data) + raw_data = tfio.genome.read_fastq(filename=fastq_path) + data = tfio.genome.sequences_to_onehot(sequences=raw_data.sequences) - assert np.all(out.to_list() == expected) + assert np.all(data.to_list() == expected) def test_genome_phred_sequences_to_probability(): @@ -210,28 +201,21 @@ def test_genome_phred_sequences_to_probability(): 0.00019952621369156986, ] - with tf.compat.v1.Session() as sess: - example_quality = tf.constant(example_quality_list) - converted_phred = tfio.genome.phred_sequences_to_probability(example_quality) - out = sess.run(converted_phred) + example_quality = tf.constant(example_quality_list) + converted_phred = tfio.genome.phred_sequences_to_probability(example_quality) # Compare flat values - assert np.allclose(out.flat_values.flatten(), expected_probabilities) + assert np.allclose( + converted_phred.flat_values.numpy().flatten(), expected_probabilities + ) # Ensure nested array lengths are correct assert np.all( - [len(a) == len(b) for a, b in zip(out.to_list(), example_quality_list)] + [ + len(a) == len(b) + for a, b in zip(converted_phred.to_list(), example_quality_list) + ] ) -def test_genome_phred_sequences_to_probability_with_other_genome_ops(): - """Test quality op in graph with read_fastq op, ensure no errors""" - with tf.compat.v1.Session() as sess: - raw_data = tfio.genome.read_fastq(filename=fastq_path) - data = tfio.genome.phred_sequences_to_probability( - phred_qualities=raw_data.raw_quality - ) - sess.run(data) - - if __name__ == "__main__": test.main() diff --git a/tests/test_genome_eager.py b/tests/test_genome_v1.py similarity index 79% rename from tests/test_genome_eager.py rename to tests/test_genome_v1.py index 1798b8914..48b2642b3 100644 --- a/tests/test_genome_eager.py +++ b/tests/test_genome_v1.py @@ -19,6 +19,8 @@ import numpy as np import tensorflow as tf + +tf.compat.v1.disable_eager_execution() import tensorflow_io as tfio # pylint: disable=wrong-import-position fastq_path = os.path.join( @@ -28,8 +30,13 @@ def test_genome_fastq_reader(): """test_genome_fastq_reader""" + g1 = tf.compat.v1.Graph() + + with g1.as_default(): + data = tfio.genome.read_fastq(filename=fastq_path) - data = tfio.genome.read_fastq(filename=fastq_path) + sess = tf.compat.v1.Session(graph=g1) + data_np = sess.run(data) data_expected = [ b"GATTACA", @@ -45,8 +52,8 @@ def test_genome_fastq_reader(): b"FAD", ] - assert np.all(data.sequences == data_expected) - assert np.all(data.raw_quality == quality_expected) + assert np.all(data_np.sequences == data_expected) + assert np.all(data_np.raw_quality == quality_expected) def test_genome_sequences_to_onehot(): @@ -182,10 +189,12 @@ def test_genome_sequences_to_onehot(): [[0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 1, 0]], ] - raw_data = tfio.genome.read_fastq(filename=fastq_path) - data = tfio.genome.sequences_to_onehot(sequences=raw_data.sequences) + with tf.compat.v1.Session() as sess: + raw_data = tfio.genome.read_fastq(filename=fastq_path) + data = tfio.genome.sequences_to_onehot(sequences=raw_data.sequences) + out = sess.run(data) - assert np.all(data.to_list() == expected) + assert np.all(out.to_list() == expected) def test_genome_phred_sequences_to_probability(): @@ -201,21 +210,28 @@ def test_genome_phred_sequences_to_probability(): 0.00019952621369156986, ] - example_quality = tf.constant(example_quality_list) - converted_phred = tfio.genome.phred_sequences_to_probability(example_quality) + with tf.compat.v1.Session() as sess: + example_quality = tf.constant(example_quality_list) + converted_phred = tfio.genome.phred_sequences_to_probability(example_quality) + out = sess.run(converted_phred) # Compare flat values - assert np.allclose( - converted_phred.flat_values.numpy().flatten(), expected_probabilities - ) + assert np.allclose(out.flat_values.flatten(), expected_probabilities) # Ensure nested array lengths are correct assert np.all( - [ - len(a) == len(b) - for a, b in zip(converted_phred.to_list(), example_quality_list) - ] + [len(a) == len(b) for a, b in zip(out.to_list(), example_quality_list)] ) +def test_genome_phred_sequences_to_probability_with_other_genome_ops(): + """Test quality op in graph with read_fastq op, ensure no errors""" + with tf.compat.v1.Session() as sess: + raw_data = tfio.genome.read_fastq(filename=fastq_path) + data = tfio.genome.phred_sequences_to_probability( + phred_qualities=raw_data.raw_quality + ) + sess.run(data) + + if __name__ == "__main__": test.main() diff --git a/tests/test_hdf5_eager.py b/tests/test_hdf5.py similarity index 100% rename from tests/test_hdf5_eager.py rename to tests/test_hdf5.py diff --git a/tests/test_hdfs_eager.py b/tests/test_hdfs.py similarity index 100% rename from tests/test_hdfs_eager.py rename to tests/test_hdfs.py diff --git a/tests/test_http_eager.py b/tests/test_http.py similarity index 100% rename from tests/test_http_eager.py rename to tests/test_http.py diff --git a/tests/test_ignite.py b/tests/test_ignite_v1.py similarity index 100% rename from tests/test_ignite.py rename to tests/test_ignite_v1.py diff --git a/tests/test_image_eager.py b/tests/test_image.py similarity index 100% rename from tests/test_image_eager.py rename to tests/test_image.py diff --git a/tests/test_io_dataset_eager.py b/tests/test_io_dataset.py similarity index 100% rename from tests/test_io_dataset_eager.py rename to tests/test_io_dataset.py diff --git a/tests/test_io_layer_eager.py b/tests/test_io_layer.py similarity index 100% rename from tests/test_io_layer_eager.py rename to tests/test_io_layer.py diff --git a/tests/test_io_tensor_eager.py b/tests/test_io_tensor.py similarity index 100% rename from tests/test_io_tensor_eager.py rename to tests/test_io_tensor.py diff --git a/tests/test_json_eager.py b/tests/test_json.py similarity index 100% rename from tests/test_json_eager.py rename to tests/test_json.py diff --git a/tests/test_kafka.py b/tests/test_kafka.py index 8c539473c..06e82b12a 100644 --- a/tests/test_kafka.py +++ b/tests/test_kafka.py @@ -1,4 +1,4 @@ -# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# Copyright 2018 The TensorFlow Authors. All Rights Reserved. # # 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 @@ -12,500 +12,504 @@ # License for the specific language governing permissions and limitations under # the License. # ============================================================================== -"""Tests for KafkaDataset.""" +"""Tests for Kafka Output Sequence.""" import time import pytest +import numpy as np +import threading import tensorflow as tf - -tf.compat.v1.disable_eager_execution() - -from tensorflow import dtypes # pylint: disable=wrong-import-position -from tensorflow import errors # pylint: disable=wrong-import-position -from tensorflow import test # pylint: disable=wrong-import-position -from tensorflow.compat.v1 import data # pylint: disable=wrong-import-position - +import tensorflow_io as tfio +from tensorflow_io.kafka.python.ops import ( + kafka_ops, +) # pylint: disable=wrong-import-position import tensorflow_io.kafka as kafka_io # pylint: disable=wrong-import-position -class KafkaDatasetTest(test.TestCase): - """Tests for KafkaDataset.""" - - # The Kafka server has to be setup before the test - # and tear down after the test manually. - # The docker engine has to be installed. - # - # To setup the Kafka server: - # $ bash kafka_test.sh start kafka - # - # To tear down the Kafka server: - # $ bash kafka_test.sh stop kafka - - def test_kafka_dataset(self): - """Tests for KafkaDataset when reading non-keyed messages - from a single-partitioned topic""" - topics = tf.compat.v1.placeholder(dtypes.string, shape=[None]) - num_epochs = tf.compat.v1.placeholder(dtypes.int64, shape=[]) - batch_size = tf.compat.v1.placeholder(dtypes.int64, shape=[]) - - repeat_dataset = kafka_io.KafkaDataset(topics, group="test", eof=True).repeat( - num_epochs +def test_kafka_io_tensor(): + kafka = tfio.IOTensor.from_kafka("test") + assert kafka.dtype == tf.string + assert kafka.shape.as_list() == [None] + assert np.all( + kafka.to_tensor().numpy() == [("D" + str(i)).encode() for i in range(10)] + ) + assert len(kafka.to_tensor()) == 10 + + +@pytest.mark.skip(reason="TODO") +def test_kafka_output_sequence(): + """Test case based on fashion mnist tutorial""" + fashion_mnist = tf.keras.datasets.fashion_mnist + ((train_images, train_labels), (test_images, _)) = fashion_mnist.load_data() + + class_names = [ + "T-shirt/top", + "Trouser", + "Pullover", + "Dress", + "Coat", + "Sandal", + "Shirt", + "Sneaker", + "Bag", + "Ankle boot", + ] + + train_images = train_images / 255.0 + test_images = test_images / 255.0 + + model = tf.keras.Sequential( + [ + tf.keras.layers.Flatten(input_shape=(28, 28)), + tf.keras.layers.Dense(128, activation=tf.nn.relu), + tf.keras.layers.Dense(10, activation=tf.nn.softmax), + ] + ) + + model.compile( + optimizer="adam", loss="sparse_categorical_crossentropy", metrics=["accuracy"] + ) + + model.fit(train_images, train_labels, epochs=5) + + class OutputCallback(tf.keras.callbacks.Callback): + """KafkaOutputCallback""" + + def __init__( + self, batch_size, topic, servers + ): # pylint: disable=super-init-not-called + self._sequence = kafka_ops.KafkaOutputSequence(topic=topic, servers=servers) + self._batch_size = batch_size + + def on_predict_batch_end(self, batch, logs=None): + index = batch * self._batch_size + for outputs in logs["outputs"]: + for output in outputs: + self._sequence.setitem(index, class_names[np.argmax(output)]) + index += 1 + + def flush(self): + self._sequence.flush() + + channel = "e{}e".format(time.time()) + topic = "test_" + channel + + # By default batch size is 32 + output = OutputCallback(32, topic, "localhost") + predictions = model.predict(test_images, callbacks=[output]) + output.flush() + + predictions = [class_names[v] for v in np.argmax(predictions, axis=1)] + + # Reading from `test_e(time)e` we should get the same result + dataset = tfio.kafka.KafkaDataset(topics=[topic], group="test", eof=True) + for entry, prediction in zip(dataset, predictions): + assert entry.numpy() == prediction.encode() + + +def test_avro_kafka_dataset(): + """test_avro_kafka_dataset""" + schema = ( + '{"type":"record","name":"myrecord","fields":[' + '{"name":"f1","type":"string"},' + '{"name":"f2","type":"long"},' + '{"name":"f3","type":["null","string"],"default":null}' + "]}" + ) + dataset = kafka_io.KafkaDataset(["avro-test:0"], group="avro-test", eof=True) + # remove kafka framing + dataset = dataset.map(lambda e: tf.strings.substr(e, 5, -1)) + # deserialize avro + dataset = dataset.map( + lambda e: tfio.experimental.serialization.decode_avro(e, schema=schema) + ) + entries = [(e["f1"], e["f2"], e["f3"]) for e in dataset] + np.all(entries == [("value1", 1, ""), ("value2", 2, ""), ("value3", 3, "")]) + + +def test_avro_kafka_dataset_with_resource(): + """test_avro_kafka_dataset_with_resource""" + schema = ( + '{"type":"record","name":"myrecord","fields":[' + '{"name":"f1","type":"string"},' + '{"name":"f2","type":"long"},' + '{"name":"f3","type":["null","string"],"default":null}' + ']}"' + ) + schema_resource = kafka_io.decode_avro_init(schema) + dataset = kafka_io.KafkaDataset(["avro-test:0"], group="avro-test", eof=True) + # remove kafka framing + dataset = dataset.map(lambda e: tf.strings.substr(e, 5, -1)) + # deserialize avro + dataset = dataset.map( + lambda e: kafka_io.decode_avro( + e, schema=schema_resource, dtype=[tf.string, tf.int64, tf.string] ) - batch_dataset = repeat_dataset.batch(batch_size) - - iterator = data.Iterator.from_structure(batch_dataset.output_types) - init_op = iterator.make_initializer(repeat_dataset) - init_batch_op = iterator.make_initializer(batch_dataset) - get_next = iterator.get_next() - - with self.cached_session() as sess: - # Basic test: read a limited number of messages from the topic. - sess.run(init_op, feed_dict={topics: ["test:0:0:4"], num_epochs: 1}) - for i in range(5): - self.assertEqual(("D" + str(i)).encode(), sess.run(get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - # Basic test: read all the messages from the topic from offset 5. - sess.run(init_op, feed_dict={topics: ["test:0:5:-1"], num_epochs: 1}) - for i in range(5): - self.assertEqual(("D" + str(i + 5)).encode(), sess.run(get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - # Basic test: read from different subscriptions of the same topic. - sess.run( - init_op, - feed_dict={topics: ["test:0:0:4", "test:0:5:-1"], num_epochs: 1}, - ) - for j in range(2): - for i in range(5): - self.assertEqual( - ("D" + str(i + j * 5)).encode(), sess.run(get_next) - ) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - # Test repeated iteration through both subscriptions. - sess.run( - init_op, - feed_dict={topics: ["test:0:0:4", "test:0:5:-1"], num_epochs: 10}, - ) - for _ in range(10): - for j in range(2): - for i in range(5): - self.assertEqual( - ("D" + str(i + j * 5)).encode(), sess.run(get_next) - ) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - # Test batched and repeated iteration through both subscriptions. - sess.run( - init_batch_op, - feed_dict={ - topics: ["test:0:0:4", "test:0:5:-1"], - num_epochs: 10, - batch_size: 5, - }, - ) - for _ in range(10): - self.assertAllEqual( - [("D" + str(i)).encode() for i in range(5)], sess.run(get_next) - ) - self.assertAllEqual( - [("D" + str(i + 5)).encode() for i in range(5)], sess.run(get_next) - ) - - @pytest.mark.skip(reason="TODO") - def test_kafka_dataset_save_and_restore(self): - """Tests for KafkaDataset save and restore.""" - g = tf.Graph() - with g.as_default(): - topics = tf.compat.v1.placeholder(dtypes.string, shape=[None]) - num_epochs = tf.compat.v1.placeholder(dtypes.int64, shape=[]) - - repeat_dataset = kafka_io.KafkaDataset( - topics, group="test", eof=True - ).repeat(num_epochs) - iterator = repeat_dataset.make_initializable_iterator() - get_next = iterator.get_next() - - it = tf.data.experimental.make_saveable_from_iterator(iterator) - g.add_to_collection(tf.compat.v1.GraphKeys.SAVEABLE_OBJECTS, it) - saver = tf.compat.v1.train.Saver() - - model_file = "/tmp/test-kafka-model" - with self.cached_session() as sess: - sess.run( - iterator.initializer, - feed_dict={topics: ["test:0:0:4"], num_epochs: 1}, - ) - for i in range(3): - self.assertEqual(("D" + str(i)).encode(), sess.run(get_next)) - # Save current offset which is 2 - saver.save(sess, model_file, global_step=3) - - checkpoint_file = "/tmp/test-kafka-model-3" - with self.cached_session() as sess: - saver.restore(sess, checkpoint_file) - # Restore current offset to 2 - for i in [2, 3]: - self.assertEqual(("D" + str(i)).encode(), sess.run(get_next)) - - def test_kafka_topic_configuration(self): - """Tests for KafkaDataset topic configuration properties.""" - topics = tf.compat.v1.placeholder(dtypes.string, shape=[None]) - num_epochs = tf.compat.v1.placeholder(dtypes.int64, shape=[]) - cfg_list = ["auto.offset.reset=earliest"] - - repeat_dataset = kafka_io.KafkaDataset( - topics, group="test", eof=True, config_topic=cfg_list - ).repeat(num_epochs) - - iterator = data.Iterator.from_structure(repeat_dataset.output_types) - init_op = iterator.make_initializer(repeat_dataset) - get_next = iterator.get_next() - - with self.cached_session() as sess: - # Use a wrong offset 100 here to make sure - # configuration 'auto.offset.reset=earliest' works. - sess.run(init_op, feed_dict={topics: ["test:0:100:-1"], num_epochs: 1}) - for i in range(5): - self.assertEqual(("D" + str(i)).encode(), sess.run(get_next)) - - def test_kafka_global_configuration(self): - """Tests for KafkaDataset global configuration properties.""" - topics = tf.compat.v1.placeholder(dtypes.string, shape=[None]) - num_epochs = tf.compat.v1.placeholder(dtypes.int64, shape=[]) - cfg_list = ["debug=generic", "enable.auto.commit=false"] - - repeat_dataset = kafka_io.KafkaDataset( - topics, group="test", eof=True, config_global=cfg_list - ).repeat(num_epochs) - - iterator = data.Iterator.from_structure(repeat_dataset.output_types) - init_op = iterator.make_initializer(repeat_dataset) - get_next = iterator.get_next() - - with self.cached_session() as sess: - sess.run(init_op, feed_dict={topics: ["test:0:0:4"], num_epochs: 1}) - for i in range(5): - self.assertEqual(("D" + str(i)).encode(), sess.run(get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - def test_kafka_wrong_global_configuration_failed(self): - """Tests for KafkaDataset worng global configuration properties.""" - topics = tf.compat.v1.placeholder(dtypes.string, shape=[None]) - num_epochs = tf.compat.v1.placeholder(dtypes.int64, shape=[]) - - # Add wrong configuration - wrong_cfg = ["debug=al"] - repeat_dataset = kafka_io.KafkaDataset( - topics, group="test", eof=True, config_global=wrong_cfg - ).repeat(num_epochs) - - iterator = data.Iterator.from_structure(repeat_dataset.output_types) - init_op = iterator.make_initializer(repeat_dataset) - get_next = iterator.get_next() - - with self.cached_session() as sess: - sess.run(init_op, feed_dict={topics: ["test:0:0:4"], num_epochs: 1}) - with self.assertRaises(errors.InternalError): - sess.run(get_next) - - def test_kafka_wrong_topic_configuration_failed(self): - """Tests for KafkaDataset wrong topic configuration properties.""" - topics = tf.compat.v1.placeholder(dtypes.string, shape=[None]) - num_epochs = tf.compat.v1.placeholder(dtypes.int64, shape=[]) - - # Add wrong configuration - wrong_cfg = ["auto.offset.reset=arliest"] - repeat_dataset = kafka_io.KafkaDataset( - topics, group="test", eof=True, config_topic=wrong_cfg - ).repeat(num_epochs) - - iterator = data.Iterator.from_structure(repeat_dataset.output_types) - init_op = iterator.make_initializer(repeat_dataset) - get_next = iterator.get_next() - - with self.cached_session() as sess: - sess.run(init_op, feed_dict={topics: ["test:0:0:4"], num_epochs: 1}) - with self.assertRaises(errors.InternalError): - sess.run(get_next) - - def test_write_kafka(self): - """test_write_kafka""" - channel = "e{}e".format(time.time()) - - # Start with reading test topic, replace `D` with `e(time)e`, - # and write to test_e(time)e` topic. - dataset = kafka_io.KafkaDataset(topics=["test:0:0:4"], group="test", eof=True) - dataset = dataset.map( - lambda x: kafka_io.write_kafka( - tf.strings.regex_replace(x, "D", channel), topic="test_" + channel - ) + ) + entries = [(f1.numpy(), f2.numpy(), f3.numpy()) for (f1, f2, f3) in dataset] + np.all(entries == [("value1", 1), ("value2", 2), ("value3", 3)]) + + +def test_kafka_stream_dataset(): + dataset = tfio.IODataset.stream().from_kafka("test").batch(2) + assert np.all( + [k.numpy().tolist() for (k, _) in dataset] + == np.asarray([("D" + str(i)).encode() for i in range(10)]).reshape((5, 2)) + ) + + +def test_kafka_io_dataset(): + dataset = tfio.IODataset.from_kafka( + "test", configuration=["fetch.min.bytes=2"] + ).batch(2) + # repeat multiple times will result in the same result + for _ in range(5): + assert np.all( + [k.numpy().tolist() for (k, _) in dataset] + == np.asarray([("D" + str(i)).encode() for i in range(10)]).reshape((5, 2)) ) - iterator = dataset.make_initializable_iterator() - init_op = iterator.initializer - get_next = iterator.get_next() - - with self.cached_session() as sess: - # Basic test: read from topic 0. - sess.run(init_op) - for i in range(5): - self.assertEqual((channel + str(i)).encode(), sess.run(get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - # Reading from `test_e(time)e` we should get the same result - dataset = kafka_io.KafkaDataset( - topics=["test_" + channel], group="test", eof=True + + +def test_avro_encode_decode(): + """test_avro_encode_decode""" + schema = ( + '{"type":"record","name":"myrecord","fields":' + '[{"name":"f1","type":"string"},{"name":"f2","type":"long"}]}' + ) + value = [("value1", 1), ("value2", 2), ("value3", 3)] + f1 = tf.cast([v[0] for v in value], tf.string) + f2 = tf.cast([v[1] for v in value], tf.int64) + message = tfio.experimental.serialization.encode_avro([f1, f2], schema=schema) + entries = tfio.experimental.serialization.decode_avro(message, schema=schema) + assert np.all(entries["f1"].numpy() == f1.numpy()) + assert np.all(entries["f2"].numpy() == f2.numpy()) + + +def test_kafka_group_io_dataset_primary_cg(): + """Test the functionality of the KafkaGroupIODataset when the consumer group + is being newly created. + + NOTE: After the kafka cluster is setup during the testing phase, 10 messages + are written to the 'key-partition-test' topic with 5 in each partition + (topic created with 2 partitions, the messages are split based on the keys). + And the same 10 messages are written into the 'key-test' topic (topic created + with 1 partition, so no splitting of the messages based on the keys). + + K0:D0, K1:D1, K0:D2, K1:D3, K0:D4, K1:D5, K0:D6, K1:D7, K0:D8, K1:D9. + + Here, messages D0, D2, D4, D6 and D8 are written into partition 0 and the rest are written + into partition 1. + + Also, since the messages are read from different partitions, the order of retrieval may not be + the same as storage. Thus, we sort and compare. + """ + dataset = tfio.experimental.streaming.KafkaGroupIODataset( + topics=["key-partition-test"], + group_id="cgtestprimary", + servers="localhost:9092", + configuration=[ + "session.timeout.ms=7000", + "max.poll.interval.ms=8000", + "auto.offset.reset=earliest", + ], + ) + assert np.all( + sorted([k.numpy() for (k, _) in dataset]) + == sorted([("D" + str(i)).encode() for i in range(10)]) + ) + + +def test_kafka_group_io_dataset_primary_cg_no_lag(): + """Test the functionality of the KafkaGroupIODataset when the + consumer group has read all the messages and committed the offsets. + """ + dataset = tfio.experimental.streaming.KafkaGroupIODataset( + topics=["key-partition-test"], + group_id="cgtestprimary", + servers="localhost:9092", + configuration=["session.timeout.ms=7000", "max.poll.interval.ms=8000"], + ) + assert np.all(sorted([k.numpy() for (k, _) in dataset]) == []) + + +def test_kafka_group_io_dataset_primary_cg_new_topic(): + """Test the functionality of the KafkaGroupIODataset when the existing + consumer group reads data from a new topic. + """ + dataset = tfio.experimental.streaming.KafkaGroupIODataset( + topics=["key-test"], + group_id="cgtestprimary", + servers="localhost:9092", + configuration=[ + "session.timeout.ms=7000", + "max.poll.interval.ms=8000", + "auto.offset.reset=earliest", + ], + ) + assert np.all( + sorted([k.numpy() for (k, _) in dataset]) + == sorted([("D" + str(i)).encode() for i in range(10)]) + ) + + +def test_kafka_group_io_dataset_resume_primary_cg(): + """Test the functionality of the KafkaGroupIODataset when the + consumer group is yet to catch up with the newly added messages only + (Instead of reading from the beginning). + """ + + # Write new messages to the topic + for i in range(10, 100): + message = "D{}".format(i) + kafka_io.write_kafka(message=message, topic="key-partition-test") + # Read only the newly sent 90 messages + dataset = tfio.experimental.streaming.KafkaGroupIODataset( + topics=["key-partition-test"], + group_id="cgtestprimary", + servers="localhost:9092", + configuration=["session.timeout.ms=7000", "max.poll.interval.ms=8000"], + ) + assert np.all( + sorted([k.numpy() for (k, _) in dataset]) + == sorted([("D" + str(i)).encode() for i in range(10, 100)]) + ) + + +def test_kafka_group_io_dataset_resume_primary_cg_new_topic(): + """Test the functionality of the KafkaGroupIODataset when the + consumer group is yet to catch up with the newly added messages only + (Instead of reading from the beginning) from the new topic. + """ + + # Write new messages to the topic + for i in range(10, 100): + message = "D{}".format(i) + kafka_io.write_kafka(message=message, topic="key-test") + # Read only the newly sent 90 messages + dataset = tfio.experimental.streaming.KafkaGroupIODataset( + topics=["key-test"], + group_id="cgtestprimary", + servers="localhost:9092", + configuration=["session.timeout.ms=7000", "max.poll.interval.ms=8000"], + ) + assert np.all( + sorted([k.numpy() for (k, _) in dataset]) + == sorted([("D" + str(i)).encode() for i in range(10, 100)]) + ) + + +def test_kafka_group_io_dataset_secondary_cg(): + """Test the functionality of the KafkaGroupIODataset when a + secondary consumer group is created and is yet to catch up all the messages, + from the beginning. + """ + + dataset = tfio.experimental.streaming.KafkaGroupIODataset( + topics=["key-partition-test"], + group_id="cgtestsecondary", + servers="localhost:9092", + configuration=[ + "session.timeout.ms=7000", + "max.poll.interval.ms=8000", + "auto.offset.reset=earliest", + ], + ) + assert np.all( + sorted([k.numpy() for (k, _) in dataset]) + == sorted([("D" + str(i)).encode() for i in range(100)]) + ) + + +def test_kafka_group_io_dataset_tertiary_cg_multiple_topics(): + """Test the functionality of the KafkaGroupIODataset when a new + consumer group reads data from multiple topics from the beginning. + """ + + dataset = tfio.experimental.streaming.KafkaGroupIODataset( + topics=["key-partition-test", "key-test"], + group_id="cgtesttertiary", + servers="localhost:9092", + configuration=[ + "session.timeout.ms=7000", + "max.poll.interval.ms=8000", + "auto.offset.reset=earliest", + ], + ) + assert np.all( + sorted([k.numpy() for (k, _) in dataset]) + == sorted([("D" + str(i)).encode() for i in range(100)] * 2) + ) + + +def test_kafka_group_io_dataset_auto_offset_reset(): + """Test the functionality of the `auto.offset.reset` configuration + at global and topic level""" + + dataset = tfio.experimental.streaming.KafkaGroupIODataset( + topics=["key-partition-test"], + group_id="cgglobaloffsetearliest", + servers="localhost:9092", + configuration=[ + "session.timeout.ms=7000", + "max.poll.interval.ms=8000", + "auto.offset.reset=earliest", + ], + ) + assert np.all( + sorted([k.numpy() for (k, _) in dataset]) + == sorted([("D" + str(i)).encode() for i in range(100)]) + ) + + dataset = tfio.experimental.streaming.KafkaGroupIODataset( + topics=["key-partition-test"], + group_id="cgglobaloffsetlatest", + servers="localhost:9092", + configuration=[ + "session.timeout.ms=7000", + "max.poll.interval.ms=8000", + "auto.offset.reset=latest", + ], + ) + assert np.all(sorted([k.numpy() for (k, _) in dataset]) == []) + + dataset = tfio.experimental.streaming.KafkaGroupIODataset( + topics=["key-partition-test"], + group_id="cgtopicoffsetearliest", + servers="localhost:9092", + configuration=[ + "session.timeout.ms=7000", + "max.poll.interval.ms=8000", + "conf.topic.auto.offset.reset=earliest", + ], + ) + assert np.all( + sorted([k.numpy() for (k, _) in dataset]) + == sorted([("D" + str(i)).encode() for i in range(100)]) + ) + + dataset = tfio.experimental.streaming.KafkaGroupIODataset( + topics=["key-partition-test"], + group_id="cgtopicoffsetlatest", + servers="localhost:9092", + configuration=[ + "session.timeout.ms=7000", + "max.poll.interval.ms=8000", + "conf.topic.auto.offset.reset=latest", + ], + ) + assert np.all(sorted([k.numpy() for (k, _) in dataset]) == []) + + +def test_kafka_group_io_dataset_invalid_stream_timeout(): + """Test the functionality of the KafkaGroupIODataset when the + consumer is configured to have an invalid stream_timeout value which is + less than the message_timeout value. + NOTE: The default value for message_timeout=5000 + """ + + STREAM_TIMEOUT = -20 + try: + tfio.experimental.streaming.KafkaGroupIODataset( + topics=["key-partition-test", "key-test"], + group_id="cgteststreaminvalid", + servers="localhost:9092", + stream_timeout=STREAM_TIMEOUT, + configuration=["session.timeout.ms=7000", "max.poll.interval.ms=8000"], ) - iterator = dataset.make_initializable_iterator() - init_op = iterator.initializer - get_next = iterator.get_next() - - with self.cached_session() as sess: - sess.run(init_op) - for i in range(5): - self.assertEqual((channel + str(i)).encode(), sess.run(get_next)) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - def test_kafka_dataset_with_key(self): - """Tests for KafkaDataset when reading keyed-messages - from a single-partitioned topic""" - topics = tf.compat.v1.placeholder(dtypes.string, shape=[None]) - num_epochs = tf.compat.v1.placeholder(dtypes.int64, shape=[]) - batch_size = tf.compat.v1.placeholder(dtypes.int64, shape=[]) - - repeat_dataset = kafka_io.KafkaDataset( - topics, group="test", eof=True, message_key=True - ).repeat(num_epochs) - batch_dataset = repeat_dataset.batch(batch_size) - - iterator = data.Iterator.from_structure(batch_dataset.output_types) - init_op = iterator.make_initializer(repeat_dataset) - init_batch_op = iterator.make_initializer(batch_dataset) - get_next = iterator.get_next() - - with self.cached_session() as sess: - # Basic test: read a limited number of keyed messages from the topic. - sess.run(init_op, feed_dict={topics: ["key-test:0:0:4"], num_epochs: 1}) - for i in range(5): - self.assertEqual( - (("D" + str(i)).encode(), ("K" + str(i % 2)).encode()), - sess.run(get_next), - ) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - # Basic test: read all the keyed messages from the topic from offset 5. - sess.run(init_op, feed_dict={topics: ["key-test:0:5:-1"], num_epochs: 1}) - for i in range(5): - self.assertEqual( - (("D" + str(i + 5)).encode(), ("K" + str((i + 5) % 2)).encode()), - sess.run(get_next), - ) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - # Basic test: read from different subscriptions of the same topic. - sess.run( - init_op, - feed_dict={ - topics: ["key-test:0:0:4", "key-test:0:5:-1"], - num_epochs: 1, - }, - ) - for j in range(2): - for i in range(5): - self.assertEqual( - ( - ("D" + str(i + j * 5)).encode(), - ("K" + str((i + j * 5) % 2)).encode(), - ), - sess.run(get_next), - ) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - # Test repeated iteration through both subscriptions. - sess.run( - init_op, - feed_dict={ - topics: ["key-test:0:0:4", "key-test:0:5:-1"], - num_epochs: 10, - }, - ) - for _ in range(10): - for j in range(2): - for i in range(5): - self.assertEqual( - ( - ("D" + str(i + j * 5)).encode(), - ("K" + str((i + j * 5) % 2)).encode(), - ), - sess.run(get_next), - ) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - # Test batched and repeated iteration through both subscriptions. - sess.run( - init_batch_op, - feed_dict={ - topics: ["key-test:0:0:4", "key-test:0:5:-1"], - num_epochs: 10, - batch_size: 5, - }, - ) - for _ in range(10): - self.assertAllEqual( - [ - [("D" + str(i)).encode() for i in range(5)], - [("K" + str(i % 2)).encode() for i in range(5)], - ], - sess.run(get_next), - ) - self.assertAllEqual( - [ - [("D" + str(i + 5)).encode() for i in range(5)], - [("K" + str((i + 5) % 2)).encode() for i in range(5)], - ], - sess.run(get_next), - ) - - def test_kafka_dataset_with_partitioned_key(self): - """Tests for KafkaDataset when reading keyed-messages - from a multi-partitioned topic""" - topics = tf.compat.v1.placeholder(dtypes.string, shape=[None]) - num_epochs = tf.compat.v1.placeholder(dtypes.int64, shape=[]) - batch_size = tf.compat.v1.placeholder(dtypes.int64, shape=[]) - - repeat_dataset = kafka_io.KafkaDataset( - topics, group="test", eof=True, message_key=True - ).repeat(num_epochs) - batch_dataset = repeat_dataset.batch(batch_size) - - iterator = data.Iterator.from_structure(batch_dataset.output_types) - init_op = iterator.make_initializer(repeat_dataset) - init_batch_op = iterator.make_initializer(batch_dataset) - get_next = iterator.get_next() - - with self.cached_session() as sess: - # Basic test: read first 5 messages from the first partition of the topic. - # NOTE: The key-partition mapping occurs based on the order in which the data - # is being stored in kafka. Please check kafka_test.sh for the sample data. - - sess.run( - init_op, - feed_dict={topics: ["key-partition-test:0:0:5"], num_epochs: 1}, - ) - for i in range(5): - self.assertEqual( - (("D" + str(i * 2)).encode(), (b"K0")), sess.run(get_next), - ) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - # Basic test: read first 5 messages from the second partition of the topic. - sess.run( - init_op, - feed_dict={topics: ["key-partition-test:1:0:5"], num_epochs: 1}, - ) - for i in range(5): - self.assertEqual( - (("D" + str(i * 2 + 1)).encode(), (b"K1")), sess.run(get_next), - ) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - # Basic test: read from different subscriptions to the same topic. - sess.run( - init_op, - feed_dict={ - topics: ["key-partition-test:0:0:5", "key-partition-test:1:0:5"], - num_epochs: 1, - }, - ) - for j in range(2): - for i in range(5): - self.assertEqual( - (("D" + str(i * 2 + j)).encode(), ("K" + str(j)).encode()), - sess.run(get_next), - ) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - # Test repeated iteration through both subscriptions. - sess.run( - init_op, - feed_dict={ - topics: ["key-partition-test:0:0:5", "key-partition-test:1:0:5"], - num_epochs: 10, - }, - ) - for _ in range(10): - for j in range(2): - for i in range(5): - self.assertEqual( - (("D" + str(i * 2 + j)).encode(), ("K" + str(j)).encode()), - sess.run(get_next), - ) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - # Test batched and repeated iteration through both subscriptions. - sess.run( - init_batch_op, - feed_dict={ - topics: ["key-partition-test:0:0:5", "key-partition-test:1:0:5"], - num_epochs: 10, - batch_size: 5, - }, + except ValueError as e: + assert str( + e + ) == "Invalid stream_timeout value: {} ,set it to -1 to block indefinitely.".format( + STREAM_TIMEOUT + ) + + +def test_kafka_group_io_dataset_stream_timeout_check(): + """Test the functionality of the KafkaGroupIODataset when the + consumer is configured to have a valid stream_timeout value and thus waits + for the new messages from kafka. + NOTE: The default value for message_timeout=5000 + """ + + def write_messages_background(): + # Write new messages to the topic in a background thread + time.sleep(6) + for i in range(100, 200): + message = "D{}".format(i) + kafka_io.write_kafka(message=message, topic="key-partition-test") + + dataset = tfio.experimental.streaming.KafkaGroupIODataset( + topics=["key-partition-test"], + group_id="cgteststreamvalid", + servers="localhost:9092", + stream_timeout=20000, + configuration=[ + "session.timeout.ms=7000", + "max.poll.interval.ms=8000", + "auto.offset.reset=earliest", + ], + ) + + # start writing the new messages to kafka using the background job. + # the job sleeps for some time (< stream_timeout) and then writes the + # messages into the topic. + thread = threading.Thread(target=write_messages_background, args=()) + thread.daemon = True + thread.start() + + # At the end, after the timeout has occurred, we must have the old 100 messages + # along with the new 100 messages + assert np.all( + sorted([k.numpy() for (k, _) in dataset]) + == sorted([("D" + str(i)).encode() for i in range(200)]) + ) + + +def test_kafka_batch_io_dataset(): + """Test the functionality of the KafkaBatchIODataset by training a model + directly on the incoming kafka message batch(of type tf.data.Dataset), in an + online-training fashion. + + NOTE: This kind of dataset is suitable in scenarios where the 'keys' of 'messages' + act as labels. If not, additional transformations are required. + """ + + dataset = tfio.experimental.streaming.KafkaBatchIODataset( + topics=["mini-batch-test"], + group_id="cgminibatch", + servers=None, + stream_timeout=5000, + configuration=[ + "session.timeout.ms=7000", + "max.poll.interval.ms=8000", + "auto.offset.reset=earliest", + ], + ) + + NUM_COLUMNS = 1 + model = tf.keras.Sequential( + [ + tf.keras.layers.Input(shape=(NUM_COLUMNS,)), + tf.keras.layers.Dense(4, activation="relu"), + tf.keras.layers.Dropout(0.1), + tf.keras.layers.Dense(1, activation="sigmoid"), + ] + ) + model.compile( + optimizer="adam", + loss=tf.keras.losses.BinaryCrossentropy(from_logits=True), + metrics=["accuracy"], + ) + assert issubclass(type(dataset), tf.data.Dataset) + for mini_d in dataset: + mini_d = mini_d.map( + lambda m, k: ( + tf.strings.to_number(m, out_type=tf.float32), + tf.strings.to_number(k, out_type=tf.float32), ) - for _ in range(10): - for j in range(2): - self.assertAllEqual( - [ - [("D" + str(i * 2 + j)).encode() for i in range(5)], - [("K" + str(j)).encode() for i in range(5)], - ], - sess.run(get_next), - ) - - def test_kafka_dataset_with_offset(self): - """Tests for KafkaDataset when reading non-keyed messages - from a single-partitioned topic""" - topics = tf.compat.v1.placeholder(dtypes.string, shape=[None]) - num_epochs = tf.compat.v1.placeholder(dtypes.int64, shape=[]) - batch_size = tf.compat.v1.placeholder(dtypes.int64, shape=[]) - - repeat_dataset = kafka_io.KafkaDataset( - topics, group="test", eof=True, message_offset=True - ).repeat(num_epochs) - batch_dataset = repeat_dataset.batch(batch_size) - - iterator = data.Iterator.from_structure(batch_dataset.output_types) - init_op = iterator.make_initializer(repeat_dataset) - get_next = iterator.get_next() - - with self.cached_session() as sess: - # Basic offset test: read a limited number of messages from the topic. - sess.run(init_op, feed_dict={topics: ["offset-test:0:0:4"], num_epochs: 1}) - for i in range(5): - self.assertEqual( - (("D" + str(i)).encode(), ("0:" + str(i)).encode()), - sess.run(get_next), - ) - with self.assertRaises(errors.OutOfRangeError): - sess.run(get_next) - - -if __name__ == "__main__": - test.main() + ).batch(2) + assert issubclass(type(mini_d), tf.data.Dataset) + # Fits the model as long as the data keeps on streaming + model.fit(mini_d, epochs=5) diff --git a/tests/test_kafka_eager.py b/tests/test_kafka_eager.py deleted file mode 100644 index 06e82b12a..000000000 --- a/tests/test_kafka_eager.py +++ /dev/null @@ -1,515 +0,0 @@ -# Copyright 2018 The TensorFlow Authors. All Rights Reserved. -# -# 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 Kafka Output Sequence.""" - - -import time -import pytest -import numpy as np -import threading - -import tensorflow as tf -import tensorflow_io as tfio -from tensorflow_io.kafka.python.ops import ( - kafka_ops, -) # pylint: disable=wrong-import-position -import tensorflow_io.kafka as kafka_io # pylint: disable=wrong-import-position - - -def test_kafka_io_tensor(): - kafka = tfio.IOTensor.from_kafka("test") - assert kafka.dtype == tf.string - assert kafka.shape.as_list() == [None] - assert np.all( - kafka.to_tensor().numpy() == [("D" + str(i)).encode() for i in range(10)] - ) - assert len(kafka.to_tensor()) == 10 - - -@pytest.mark.skip(reason="TODO") -def test_kafka_output_sequence(): - """Test case based on fashion mnist tutorial""" - fashion_mnist = tf.keras.datasets.fashion_mnist - ((train_images, train_labels), (test_images, _)) = fashion_mnist.load_data() - - class_names = [ - "T-shirt/top", - "Trouser", - "Pullover", - "Dress", - "Coat", - "Sandal", - "Shirt", - "Sneaker", - "Bag", - "Ankle boot", - ] - - train_images = train_images / 255.0 - test_images = test_images / 255.0 - - model = tf.keras.Sequential( - [ - tf.keras.layers.Flatten(input_shape=(28, 28)), - tf.keras.layers.Dense(128, activation=tf.nn.relu), - tf.keras.layers.Dense(10, activation=tf.nn.softmax), - ] - ) - - model.compile( - optimizer="adam", loss="sparse_categorical_crossentropy", metrics=["accuracy"] - ) - - model.fit(train_images, train_labels, epochs=5) - - class OutputCallback(tf.keras.callbacks.Callback): - """KafkaOutputCallback""" - - def __init__( - self, batch_size, topic, servers - ): # pylint: disable=super-init-not-called - self._sequence = kafka_ops.KafkaOutputSequence(topic=topic, servers=servers) - self._batch_size = batch_size - - def on_predict_batch_end(self, batch, logs=None): - index = batch * self._batch_size - for outputs in logs["outputs"]: - for output in outputs: - self._sequence.setitem(index, class_names[np.argmax(output)]) - index += 1 - - def flush(self): - self._sequence.flush() - - channel = "e{}e".format(time.time()) - topic = "test_" + channel - - # By default batch size is 32 - output = OutputCallback(32, topic, "localhost") - predictions = model.predict(test_images, callbacks=[output]) - output.flush() - - predictions = [class_names[v] for v in np.argmax(predictions, axis=1)] - - # Reading from `test_e(time)e` we should get the same result - dataset = tfio.kafka.KafkaDataset(topics=[topic], group="test", eof=True) - for entry, prediction in zip(dataset, predictions): - assert entry.numpy() == prediction.encode() - - -def test_avro_kafka_dataset(): - """test_avro_kafka_dataset""" - schema = ( - '{"type":"record","name":"myrecord","fields":[' - '{"name":"f1","type":"string"},' - '{"name":"f2","type":"long"},' - '{"name":"f3","type":["null","string"],"default":null}' - "]}" - ) - dataset = kafka_io.KafkaDataset(["avro-test:0"], group="avro-test", eof=True) - # remove kafka framing - dataset = dataset.map(lambda e: tf.strings.substr(e, 5, -1)) - # deserialize avro - dataset = dataset.map( - lambda e: tfio.experimental.serialization.decode_avro(e, schema=schema) - ) - entries = [(e["f1"], e["f2"], e["f3"]) for e in dataset] - np.all(entries == [("value1", 1, ""), ("value2", 2, ""), ("value3", 3, "")]) - - -def test_avro_kafka_dataset_with_resource(): - """test_avro_kafka_dataset_with_resource""" - schema = ( - '{"type":"record","name":"myrecord","fields":[' - '{"name":"f1","type":"string"},' - '{"name":"f2","type":"long"},' - '{"name":"f3","type":["null","string"],"default":null}' - ']}"' - ) - schema_resource = kafka_io.decode_avro_init(schema) - dataset = kafka_io.KafkaDataset(["avro-test:0"], group="avro-test", eof=True) - # remove kafka framing - dataset = dataset.map(lambda e: tf.strings.substr(e, 5, -1)) - # deserialize avro - dataset = dataset.map( - lambda e: kafka_io.decode_avro( - e, schema=schema_resource, dtype=[tf.string, tf.int64, tf.string] - ) - ) - entries = [(f1.numpy(), f2.numpy(), f3.numpy()) for (f1, f2, f3) in dataset] - np.all(entries == [("value1", 1), ("value2", 2), ("value3", 3)]) - - -def test_kafka_stream_dataset(): - dataset = tfio.IODataset.stream().from_kafka("test").batch(2) - assert np.all( - [k.numpy().tolist() for (k, _) in dataset] - == np.asarray([("D" + str(i)).encode() for i in range(10)]).reshape((5, 2)) - ) - - -def test_kafka_io_dataset(): - dataset = tfio.IODataset.from_kafka( - "test", configuration=["fetch.min.bytes=2"] - ).batch(2) - # repeat multiple times will result in the same result - for _ in range(5): - assert np.all( - [k.numpy().tolist() for (k, _) in dataset] - == np.asarray([("D" + str(i)).encode() for i in range(10)]).reshape((5, 2)) - ) - - -def test_avro_encode_decode(): - """test_avro_encode_decode""" - schema = ( - '{"type":"record","name":"myrecord","fields":' - '[{"name":"f1","type":"string"},{"name":"f2","type":"long"}]}' - ) - value = [("value1", 1), ("value2", 2), ("value3", 3)] - f1 = tf.cast([v[0] for v in value], tf.string) - f2 = tf.cast([v[1] for v in value], tf.int64) - message = tfio.experimental.serialization.encode_avro([f1, f2], schema=schema) - entries = tfio.experimental.serialization.decode_avro(message, schema=schema) - assert np.all(entries["f1"].numpy() == f1.numpy()) - assert np.all(entries["f2"].numpy() == f2.numpy()) - - -def test_kafka_group_io_dataset_primary_cg(): - """Test the functionality of the KafkaGroupIODataset when the consumer group - is being newly created. - - NOTE: After the kafka cluster is setup during the testing phase, 10 messages - are written to the 'key-partition-test' topic with 5 in each partition - (topic created with 2 partitions, the messages are split based on the keys). - And the same 10 messages are written into the 'key-test' topic (topic created - with 1 partition, so no splitting of the messages based on the keys). - - K0:D0, K1:D1, K0:D2, K1:D3, K0:D4, K1:D5, K0:D6, K1:D7, K0:D8, K1:D9. - - Here, messages D0, D2, D4, D6 and D8 are written into partition 0 and the rest are written - into partition 1. - - Also, since the messages are read from different partitions, the order of retrieval may not be - the same as storage. Thus, we sort and compare. - """ - dataset = tfio.experimental.streaming.KafkaGroupIODataset( - topics=["key-partition-test"], - group_id="cgtestprimary", - servers="localhost:9092", - configuration=[ - "session.timeout.ms=7000", - "max.poll.interval.ms=8000", - "auto.offset.reset=earliest", - ], - ) - assert np.all( - sorted([k.numpy() for (k, _) in dataset]) - == sorted([("D" + str(i)).encode() for i in range(10)]) - ) - - -def test_kafka_group_io_dataset_primary_cg_no_lag(): - """Test the functionality of the KafkaGroupIODataset when the - consumer group has read all the messages and committed the offsets. - """ - dataset = tfio.experimental.streaming.KafkaGroupIODataset( - topics=["key-partition-test"], - group_id="cgtestprimary", - servers="localhost:9092", - configuration=["session.timeout.ms=7000", "max.poll.interval.ms=8000"], - ) - assert np.all(sorted([k.numpy() for (k, _) in dataset]) == []) - - -def test_kafka_group_io_dataset_primary_cg_new_topic(): - """Test the functionality of the KafkaGroupIODataset when the existing - consumer group reads data from a new topic. - """ - dataset = tfio.experimental.streaming.KafkaGroupIODataset( - topics=["key-test"], - group_id="cgtestprimary", - servers="localhost:9092", - configuration=[ - "session.timeout.ms=7000", - "max.poll.interval.ms=8000", - "auto.offset.reset=earliest", - ], - ) - assert np.all( - sorted([k.numpy() for (k, _) in dataset]) - == sorted([("D" + str(i)).encode() for i in range(10)]) - ) - - -def test_kafka_group_io_dataset_resume_primary_cg(): - """Test the functionality of the KafkaGroupIODataset when the - consumer group is yet to catch up with the newly added messages only - (Instead of reading from the beginning). - """ - - # Write new messages to the topic - for i in range(10, 100): - message = "D{}".format(i) - kafka_io.write_kafka(message=message, topic="key-partition-test") - # Read only the newly sent 90 messages - dataset = tfio.experimental.streaming.KafkaGroupIODataset( - topics=["key-partition-test"], - group_id="cgtestprimary", - servers="localhost:9092", - configuration=["session.timeout.ms=7000", "max.poll.interval.ms=8000"], - ) - assert np.all( - sorted([k.numpy() for (k, _) in dataset]) - == sorted([("D" + str(i)).encode() for i in range(10, 100)]) - ) - - -def test_kafka_group_io_dataset_resume_primary_cg_new_topic(): - """Test the functionality of the KafkaGroupIODataset when the - consumer group is yet to catch up with the newly added messages only - (Instead of reading from the beginning) from the new topic. - """ - - # Write new messages to the topic - for i in range(10, 100): - message = "D{}".format(i) - kafka_io.write_kafka(message=message, topic="key-test") - # Read only the newly sent 90 messages - dataset = tfio.experimental.streaming.KafkaGroupIODataset( - topics=["key-test"], - group_id="cgtestprimary", - servers="localhost:9092", - configuration=["session.timeout.ms=7000", "max.poll.interval.ms=8000"], - ) - assert np.all( - sorted([k.numpy() for (k, _) in dataset]) - == sorted([("D" + str(i)).encode() for i in range(10, 100)]) - ) - - -def test_kafka_group_io_dataset_secondary_cg(): - """Test the functionality of the KafkaGroupIODataset when a - secondary consumer group is created and is yet to catch up all the messages, - from the beginning. - """ - - dataset = tfio.experimental.streaming.KafkaGroupIODataset( - topics=["key-partition-test"], - group_id="cgtestsecondary", - servers="localhost:9092", - configuration=[ - "session.timeout.ms=7000", - "max.poll.interval.ms=8000", - "auto.offset.reset=earliest", - ], - ) - assert np.all( - sorted([k.numpy() for (k, _) in dataset]) - == sorted([("D" + str(i)).encode() for i in range(100)]) - ) - - -def test_kafka_group_io_dataset_tertiary_cg_multiple_topics(): - """Test the functionality of the KafkaGroupIODataset when a new - consumer group reads data from multiple topics from the beginning. - """ - - dataset = tfio.experimental.streaming.KafkaGroupIODataset( - topics=["key-partition-test", "key-test"], - group_id="cgtesttertiary", - servers="localhost:9092", - configuration=[ - "session.timeout.ms=7000", - "max.poll.interval.ms=8000", - "auto.offset.reset=earliest", - ], - ) - assert np.all( - sorted([k.numpy() for (k, _) in dataset]) - == sorted([("D" + str(i)).encode() for i in range(100)] * 2) - ) - - -def test_kafka_group_io_dataset_auto_offset_reset(): - """Test the functionality of the `auto.offset.reset` configuration - at global and topic level""" - - dataset = tfio.experimental.streaming.KafkaGroupIODataset( - topics=["key-partition-test"], - group_id="cgglobaloffsetearliest", - servers="localhost:9092", - configuration=[ - "session.timeout.ms=7000", - "max.poll.interval.ms=8000", - "auto.offset.reset=earliest", - ], - ) - assert np.all( - sorted([k.numpy() for (k, _) in dataset]) - == sorted([("D" + str(i)).encode() for i in range(100)]) - ) - - dataset = tfio.experimental.streaming.KafkaGroupIODataset( - topics=["key-partition-test"], - group_id="cgglobaloffsetlatest", - servers="localhost:9092", - configuration=[ - "session.timeout.ms=7000", - "max.poll.interval.ms=8000", - "auto.offset.reset=latest", - ], - ) - assert np.all(sorted([k.numpy() for (k, _) in dataset]) == []) - - dataset = tfio.experimental.streaming.KafkaGroupIODataset( - topics=["key-partition-test"], - group_id="cgtopicoffsetearliest", - servers="localhost:9092", - configuration=[ - "session.timeout.ms=7000", - "max.poll.interval.ms=8000", - "conf.topic.auto.offset.reset=earliest", - ], - ) - assert np.all( - sorted([k.numpy() for (k, _) in dataset]) - == sorted([("D" + str(i)).encode() for i in range(100)]) - ) - - dataset = tfio.experimental.streaming.KafkaGroupIODataset( - topics=["key-partition-test"], - group_id="cgtopicoffsetlatest", - servers="localhost:9092", - configuration=[ - "session.timeout.ms=7000", - "max.poll.interval.ms=8000", - "conf.topic.auto.offset.reset=latest", - ], - ) - assert np.all(sorted([k.numpy() for (k, _) in dataset]) == []) - - -def test_kafka_group_io_dataset_invalid_stream_timeout(): - """Test the functionality of the KafkaGroupIODataset when the - consumer is configured to have an invalid stream_timeout value which is - less than the message_timeout value. - NOTE: The default value for message_timeout=5000 - """ - - STREAM_TIMEOUT = -20 - try: - tfio.experimental.streaming.KafkaGroupIODataset( - topics=["key-partition-test", "key-test"], - group_id="cgteststreaminvalid", - servers="localhost:9092", - stream_timeout=STREAM_TIMEOUT, - configuration=["session.timeout.ms=7000", "max.poll.interval.ms=8000"], - ) - except ValueError as e: - assert str( - e - ) == "Invalid stream_timeout value: {} ,set it to -1 to block indefinitely.".format( - STREAM_TIMEOUT - ) - - -def test_kafka_group_io_dataset_stream_timeout_check(): - """Test the functionality of the KafkaGroupIODataset when the - consumer is configured to have a valid stream_timeout value and thus waits - for the new messages from kafka. - NOTE: The default value for message_timeout=5000 - """ - - def write_messages_background(): - # Write new messages to the topic in a background thread - time.sleep(6) - for i in range(100, 200): - message = "D{}".format(i) - kafka_io.write_kafka(message=message, topic="key-partition-test") - - dataset = tfio.experimental.streaming.KafkaGroupIODataset( - topics=["key-partition-test"], - group_id="cgteststreamvalid", - servers="localhost:9092", - stream_timeout=20000, - configuration=[ - "session.timeout.ms=7000", - "max.poll.interval.ms=8000", - "auto.offset.reset=earliest", - ], - ) - - # start writing the new messages to kafka using the background job. - # the job sleeps for some time (< stream_timeout) and then writes the - # messages into the topic. - thread = threading.Thread(target=write_messages_background, args=()) - thread.daemon = True - thread.start() - - # At the end, after the timeout has occurred, we must have the old 100 messages - # along with the new 100 messages - assert np.all( - sorted([k.numpy() for (k, _) in dataset]) - == sorted([("D" + str(i)).encode() for i in range(200)]) - ) - - -def test_kafka_batch_io_dataset(): - """Test the functionality of the KafkaBatchIODataset by training a model - directly on the incoming kafka message batch(of type tf.data.Dataset), in an - online-training fashion. - - NOTE: This kind of dataset is suitable in scenarios where the 'keys' of 'messages' - act as labels. If not, additional transformations are required. - """ - - dataset = tfio.experimental.streaming.KafkaBatchIODataset( - topics=["mini-batch-test"], - group_id="cgminibatch", - servers=None, - stream_timeout=5000, - configuration=[ - "session.timeout.ms=7000", - "max.poll.interval.ms=8000", - "auto.offset.reset=earliest", - ], - ) - - NUM_COLUMNS = 1 - model = tf.keras.Sequential( - [ - tf.keras.layers.Input(shape=(NUM_COLUMNS,)), - tf.keras.layers.Dense(4, activation="relu"), - tf.keras.layers.Dropout(0.1), - tf.keras.layers.Dense(1, activation="sigmoid"), - ] - ) - model.compile( - optimizer="adam", - loss=tf.keras.losses.BinaryCrossentropy(from_logits=True), - metrics=["accuracy"], - ) - assert issubclass(type(dataset), tf.data.Dataset) - for mini_d in dataset: - mini_d = mini_d.map( - lambda m, k: ( - tf.strings.to_number(m, out_type=tf.float32), - tf.strings.to_number(k, out_type=tf.float32), - ) - ).batch(2) - assert issubclass(type(mini_d), tf.data.Dataset) - # Fits the model as long as the data keeps on streaming - model.fit(mini_d, epochs=5) diff --git a/tests/test_kafka_v1.py b/tests/test_kafka_v1.py new file mode 100644 index 000000000..8c539473c --- /dev/null +++ b/tests/test_kafka_v1.py @@ -0,0 +1,511 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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 KafkaDataset.""" + + +import time +import pytest + +import tensorflow as tf + +tf.compat.v1.disable_eager_execution() + +from tensorflow import dtypes # pylint: disable=wrong-import-position +from tensorflow import errors # pylint: disable=wrong-import-position +from tensorflow import test # pylint: disable=wrong-import-position +from tensorflow.compat.v1 import data # pylint: disable=wrong-import-position + +import tensorflow_io.kafka as kafka_io # pylint: disable=wrong-import-position + + +class KafkaDatasetTest(test.TestCase): + """Tests for KafkaDataset.""" + + # The Kafka server has to be setup before the test + # and tear down after the test manually. + # The docker engine has to be installed. + # + # To setup the Kafka server: + # $ bash kafka_test.sh start kafka + # + # To tear down the Kafka server: + # $ bash kafka_test.sh stop kafka + + def test_kafka_dataset(self): + """Tests for KafkaDataset when reading non-keyed messages + from a single-partitioned topic""" + topics = tf.compat.v1.placeholder(dtypes.string, shape=[None]) + num_epochs = tf.compat.v1.placeholder(dtypes.int64, shape=[]) + batch_size = tf.compat.v1.placeholder(dtypes.int64, shape=[]) + + repeat_dataset = kafka_io.KafkaDataset(topics, group="test", eof=True).repeat( + num_epochs + ) + batch_dataset = repeat_dataset.batch(batch_size) + + iterator = data.Iterator.from_structure(batch_dataset.output_types) + init_op = iterator.make_initializer(repeat_dataset) + init_batch_op = iterator.make_initializer(batch_dataset) + get_next = iterator.get_next() + + with self.cached_session() as sess: + # Basic test: read a limited number of messages from the topic. + sess.run(init_op, feed_dict={topics: ["test:0:0:4"], num_epochs: 1}) + for i in range(5): + self.assertEqual(("D" + str(i)).encode(), sess.run(get_next)) + with self.assertRaises(errors.OutOfRangeError): + sess.run(get_next) + + # Basic test: read all the messages from the topic from offset 5. + sess.run(init_op, feed_dict={topics: ["test:0:5:-1"], num_epochs: 1}) + for i in range(5): + self.assertEqual(("D" + str(i + 5)).encode(), sess.run(get_next)) + with self.assertRaises(errors.OutOfRangeError): + sess.run(get_next) + + # Basic test: read from different subscriptions of the same topic. + sess.run( + init_op, + feed_dict={topics: ["test:0:0:4", "test:0:5:-1"], num_epochs: 1}, + ) + for j in range(2): + for i in range(5): + self.assertEqual( + ("D" + str(i + j * 5)).encode(), sess.run(get_next) + ) + with self.assertRaises(errors.OutOfRangeError): + sess.run(get_next) + + # Test repeated iteration through both subscriptions. + sess.run( + init_op, + feed_dict={topics: ["test:0:0:4", "test:0:5:-1"], num_epochs: 10}, + ) + for _ in range(10): + for j in range(2): + for i in range(5): + self.assertEqual( + ("D" + str(i + j * 5)).encode(), sess.run(get_next) + ) + with self.assertRaises(errors.OutOfRangeError): + sess.run(get_next) + + # Test batched and repeated iteration through both subscriptions. + sess.run( + init_batch_op, + feed_dict={ + topics: ["test:0:0:4", "test:0:5:-1"], + num_epochs: 10, + batch_size: 5, + }, + ) + for _ in range(10): + self.assertAllEqual( + [("D" + str(i)).encode() for i in range(5)], sess.run(get_next) + ) + self.assertAllEqual( + [("D" + str(i + 5)).encode() for i in range(5)], sess.run(get_next) + ) + + @pytest.mark.skip(reason="TODO") + def test_kafka_dataset_save_and_restore(self): + """Tests for KafkaDataset save and restore.""" + g = tf.Graph() + with g.as_default(): + topics = tf.compat.v1.placeholder(dtypes.string, shape=[None]) + num_epochs = tf.compat.v1.placeholder(dtypes.int64, shape=[]) + + repeat_dataset = kafka_io.KafkaDataset( + topics, group="test", eof=True + ).repeat(num_epochs) + iterator = repeat_dataset.make_initializable_iterator() + get_next = iterator.get_next() + + it = tf.data.experimental.make_saveable_from_iterator(iterator) + g.add_to_collection(tf.compat.v1.GraphKeys.SAVEABLE_OBJECTS, it) + saver = tf.compat.v1.train.Saver() + + model_file = "/tmp/test-kafka-model" + with self.cached_session() as sess: + sess.run( + iterator.initializer, + feed_dict={topics: ["test:0:0:4"], num_epochs: 1}, + ) + for i in range(3): + self.assertEqual(("D" + str(i)).encode(), sess.run(get_next)) + # Save current offset which is 2 + saver.save(sess, model_file, global_step=3) + + checkpoint_file = "/tmp/test-kafka-model-3" + with self.cached_session() as sess: + saver.restore(sess, checkpoint_file) + # Restore current offset to 2 + for i in [2, 3]: + self.assertEqual(("D" + str(i)).encode(), sess.run(get_next)) + + def test_kafka_topic_configuration(self): + """Tests for KafkaDataset topic configuration properties.""" + topics = tf.compat.v1.placeholder(dtypes.string, shape=[None]) + num_epochs = tf.compat.v1.placeholder(dtypes.int64, shape=[]) + cfg_list = ["auto.offset.reset=earliest"] + + repeat_dataset = kafka_io.KafkaDataset( + topics, group="test", eof=True, config_topic=cfg_list + ).repeat(num_epochs) + + iterator = data.Iterator.from_structure(repeat_dataset.output_types) + init_op = iterator.make_initializer(repeat_dataset) + get_next = iterator.get_next() + + with self.cached_session() as sess: + # Use a wrong offset 100 here to make sure + # configuration 'auto.offset.reset=earliest' works. + sess.run(init_op, feed_dict={topics: ["test:0:100:-1"], num_epochs: 1}) + for i in range(5): + self.assertEqual(("D" + str(i)).encode(), sess.run(get_next)) + + def test_kafka_global_configuration(self): + """Tests for KafkaDataset global configuration properties.""" + topics = tf.compat.v1.placeholder(dtypes.string, shape=[None]) + num_epochs = tf.compat.v1.placeholder(dtypes.int64, shape=[]) + cfg_list = ["debug=generic", "enable.auto.commit=false"] + + repeat_dataset = kafka_io.KafkaDataset( + topics, group="test", eof=True, config_global=cfg_list + ).repeat(num_epochs) + + iterator = data.Iterator.from_structure(repeat_dataset.output_types) + init_op = iterator.make_initializer(repeat_dataset) + get_next = iterator.get_next() + + with self.cached_session() as sess: + sess.run(init_op, feed_dict={topics: ["test:0:0:4"], num_epochs: 1}) + for i in range(5): + self.assertEqual(("D" + str(i)).encode(), sess.run(get_next)) + with self.assertRaises(errors.OutOfRangeError): + sess.run(get_next) + + def test_kafka_wrong_global_configuration_failed(self): + """Tests for KafkaDataset worng global configuration properties.""" + topics = tf.compat.v1.placeholder(dtypes.string, shape=[None]) + num_epochs = tf.compat.v1.placeholder(dtypes.int64, shape=[]) + + # Add wrong configuration + wrong_cfg = ["debug=al"] + repeat_dataset = kafka_io.KafkaDataset( + topics, group="test", eof=True, config_global=wrong_cfg + ).repeat(num_epochs) + + iterator = data.Iterator.from_structure(repeat_dataset.output_types) + init_op = iterator.make_initializer(repeat_dataset) + get_next = iterator.get_next() + + with self.cached_session() as sess: + sess.run(init_op, feed_dict={topics: ["test:0:0:4"], num_epochs: 1}) + with self.assertRaises(errors.InternalError): + sess.run(get_next) + + def test_kafka_wrong_topic_configuration_failed(self): + """Tests for KafkaDataset wrong topic configuration properties.""" + topics = tf.compat.v1.placeholder(dtypes.string, shape=[None]) + num_epochs = tf.compat.v1.placeholder(dtypes.int64, shape=[]) + + # Add wrong configuration + wrong_cfg = ["auto.offset.reset=arliest"] + repeat_dataset = kafka_io.KafkaDataset( + topics, group="test", eof=True, config_topic=wrong_cfg + ).repeat(num_epochs) + + iterator = data.Iterator.from_structure(repeat_dataset.output_types) + init_op = iterator.make_initializer(repeat_dataset) + get_next = iterator.get_next() + + with self.cached_session() as sess: + sess.run(init_op, feed_dict={topics: ["test:0:0:4"], num_epochs: 1}) + with self.assertRaises(errors.InternalError): + sess.run(get_next) + + def test_write_kafka(self): + """test_write_kafka""" + channel = "e{}e".format(time.time()) + + # Start with reading test topic, replace `D` with `e(time)e`, + # and write to test_e(time)e` topic. + dataset = kafka_io.KafkaDataset(topics=["test:0:0:4"], group="test", eof=True) + dataset = dataset.map( + lambda x: kafka_io.write_kafka( + tf.strings.regex_replace(x, "D", channel), topic="test_" + channel + ) + ) + iterator = dataset.make_initializable_iterator() + init_op = iterator.initializer + get_next = iterator.get_next() + + with self.cached_session() as sess: + # Basic test: read from topic 0. + sess.run(init_op) + for i in range(5): + self.assertEqual((channel + str(i)).encode(), sess.run(get_next)) + with self.assertRaises(errors.OutOfRangeError): + sess.run(get_next) + + # Reading from `test_e(time)e` we should get the same result + dataset = kafka_io.KafkaDataset( + topics=["test_" + channel], group="test", eof=True + ) + iterator = dataset.make_initializable_iterator() + init_op = iterator.initializer + get_next = iterator.get_next() + + with self.cached_session() as sess: + sess.run(init_op) + for i in range(5): + self.assertEqual((channel + str(i)).encode(), sess.run(get_next)) + with self.assertRaises(errors.OutOfRangeError): + sess.run(get_next) + + def test_kafka_dataset_with_key(self): + """Tests for KafkaDataset when reading keyed-messages + from a single-partitioned topic""" + topics = tf.compat.v1.placeholder(dtypes.string, shape=[None]) + num_epochs = tf.compat.v1.placeholder(dtypes.int64, shape=[]) + batch_size = tf.compat.v1.placeholder(dtypes.int64, shape=[]) + + repeat_dataset = kafka_io.KafkaDataset( + topics, group="test", eof=True, message_key=True + ).repeat(num_epochs) + batch_dataset = repeat_dataset.batch(batch_size) + + iterator = data.Iterator.from_structure(batch_dataset.output_types) + init_op = iterator.make_initializer(repeat_dataset) + init_batch_op = iterator.make_initializer(batch_dataset) + get_next = iterator.get_next() + + with self.cached_session() as sess: + # Basic test: read a limited number of keyed messages from the topic. + sess.run(init_op, feed_dict={topics: ["key-test:0:0:4"], num_epochs: 1}) + for i in range(5): + self.assertEqual( + (("D" + str(i)).encode(), ("K" + str(i % 2)).encode()), + sess.run(get_next), + ) + with self.assertRaises(errors.OutOfRangeError): + sess.run(get_next) + + # Basic test: read all the keyed messages from the topic from offset 5. + sess.run(init_op, feed_dict={topics: ["key-test:0:5:-1"], num_epochs: 1}) + for i in range(5): + self.assertEqual( + (("D" + str(i + 5)).encode(), ("K" + str((i + 5) % 2)).encode()), + sess.run(get_next), + ) + with self.assertRaises(errors.OutOfRangeError): + sess.run(get_next) + + # Basic test: read from different subscriptions of the same topic. + sess.run( + init_op, + feed_dict={ + topics: ["key-test:0:0:4", "key-test:0:5:-1"], + num_epochs: 1, + }, + ) + for j in range(2): + for i in range(5): + self.assertEqual( + ( + ("D" + str(i + j * 5)).encode(), + ("K" + str((i + j * 5) % 2)).encode(), + ), + sess.run(get_next), + ) + with self.assertRaises(errors.OutOfRangeError): + sess.run(get_next) + + # Test repeated iteration through both subscriptions. + sess.run( + init_op, + feed_dict={ + topics: ["key-test:0:0:4", "key-test:0:5:-1"], + num_epochs: 10, + }, + ) + for _ in range(10): + for j in range(2): + for i in range(5): + self.assertEqual( + ( + ("D" + str(i + j * 5)).encode(), + ("K" + str((i + j * 5) % 2)).encode(), + ), + sess.run(get_next), + ) + with self.assertRaises(errors.OutOfRangeError): + sess.run(get_next) + + # Test batched and repeated iteration through both subscriptions. + sess.run( + init_batch_op, + feed_dict={ + topics: ["key-test:0:0:4", "key-test:0:5:-1"], + num_epochs: 10, + batch_size: 5, + }, + ) + for _ in range(10): + self.assertAllEqual( + [ + [("D" + str(i)).encode() for i in range(5)], + [("K" + str(i % 2)).encode() for i in range(5)], + ], + sess.run(get_next), + ) + self.assertAllEqual( + [ + [("D" + str(i + 5)).encode() for i in range(5)], + [("K" + str((i + 5) % 2)).encode() for i in range(5)], + ], + sess.run(get_next), + ) + + def test_kafka_dataset_with_partitioned_key(self): + """Tests for KafkaDataset when reading keyed-messages + from a multi-partitioned topic""" + topics = tf.compat.v1.placeholder(dtypes.string, shape=[None]) + num_epochs = tf.compat.v1.placeholder(dtypes.int64, shape=[]) + batch_size = tf.compat.v1.placeholder(dtypes.int64, shape=[]) + + repeat_dataset = kafka_io.KafkaDataset( + topics, group="test", eof=True, message_key=True + ).repeat(num_epochs) + batch_dataset = repeat_dataset.batch(batch_size) + + iterator = data.Iterator.from_structure(batch_dataset.output_types) + init_op = iterator.make_initializer(repeat_dataset) + init_batch_op = iterator.make_initializer(batch_dataset) + get_next = iterator.get_next() + + with self.cached_session() as sess: + # Basic test: read first 5 messages from the first partition of the topic. + # NOTE: The key-partition mapping occurs based on the order in which the data + # is being stored in kafka. Please check kafka_test.sh for the sample data. + + sess.run( + init_op, + feed_dict={topics: ["key-partition-test:0:0:5"], num_epochs: 1}, + ) + for i in range(5): + self.assertEqual( + (("D" + str(i * 2)).encode(), (b"K0")), sess.run(get_next), + ) + with self.assertRaises(errors.OutOfRangeError): + sess.run(get_next) + + # Basic test: read first 5 messages from the second partition of the topic. + sess.run( + init_op, + feed_dict={topics: ["key-partition-test:1:0:5"], num_epochs: 1}, + ) + for i in range(5): + self.assertEqual( + (("D" + str(i * 2 + 1)).encode(), (b"K1")), sess.run(get_next), + ) + with self.assertRaises(errors.OutOfRangeError): + sess.run(get_next) + + # Basic test: read from different subscriptions to the same topic. + sess.run( + init_op, + feed_dict={ + topics: ["key-partition-test:0:0:5", "key-partition-test:1:0:5"], + num_epochs: 1, + }, + ) + for j in range(2): + for i in range(5): + self.assertEqual( + (("D" + str(i * 2 + j)).encode(), ("K" + str(j)).encode()), + sess.run(get_next), + ) + with self.assertRaises(errors.OutOfRangeError): + sess.run(get_next) + + # Test repeated iteration through both subscriptions. + sess.run( + init_op, + feed_dict={ + topics: ["key-partition-test:0:0:5", "key-partition-test:1:0:5"], + num_epochs: 10, + }, + ) + for _ in range(10): + for j in range(2): + for i in range(5): + self.assertEqual( + (("D" + str(i * 2 + j)).encode(), ("K" + str(j)).encode()), + sess.run(get_next), + ) + with self.assertRaises(errors.OutOfRangeError): + sess.run(get_next) + + # Test batched and repeated iteration through both subscriptions. + sess.run( + init_batch_op, + feed_dict={ + topics: ["key-partition-test:0:0:5", "key-partition-test:1:0:5"], + num_epochs: 10, + batch_size: 5, + }, + ) + for _ in range(10): + for j in range(2): + self.assertAllEqual( + [ + [("D" + str(i * 2 + j)).encode() for i in range(5)], + [("K" + str(j)).encode() for i in range(5)], + ], + sess.run(get_next), + ) + + def test_kafka_dataset_with_offset(self): + """Tests for KafkaDataset when reading non-keyed messages + from a single-partitioned topic""" + topics = tf.compat.v1.placeholder(dtypes.string, shape=[None]) + num_epochs = tf.compat.v1.placeholder(dtypes.int64, shape=[]) + batch_size = tf.compat.v1.placeholder(dtypes.int64, shape=[]) + + repeat_dataset = kafka_io.KafkaDataset( + topics, group="test", eof=True, message_offset=True + ).repeat(num_epochs) + batch_dataset = repeat_dataset.batch(batch_size) + + iterator = data.Iterator.from_structure(batch_dataset.output_types) + init_op = iterator.make_initializer(repeat_dataset) + get_next = iterator.get_next() + + with self.cached_session() as sess: + # Basic offset test: read a limited number of messages from the topic. + sess.run(init_op, feed_dict={topics: ["offset-test:0:0:4"], num_epochs: 1}) + for i in range(5): + self.assertEqual( + (("D" + str(i)).encode(), ("0:" + str(i)).encode()), + sess.run(get_next), + ) + with self.assertRaises(errors.OutOfRangeError): + sess.run(get_next) + + +if __name__ == "__main__": + test.main() diff --git a/tests/test_libsvm_eager.py b/tests/test_libsvm.py similarity index 100% rename from tests/test_libsvm_eager.py rename to tests/test_libsvm.py diff --git a/tests/test_lmdb_eager.py b/tests/test_lmdb.py similarity index 100% rename from tests/test_lmdb_eager.py rename to tests/test_lmdb.py diff --git a/tests/test_mongodb_eager.py b/tests/test_mongodb.py similarity index 100% rename from tests/test_mongodb_eager.py rename to tests/test_mongodb.py diff --git a/tests/test_parquet_eager.py b/tests/test_parquet.py similarity index 100% rename from tests/test_parquet_eager.py rename to tests/test_parquet.py diff --git a/tests/test_parse_avro_eager.py b/tests/test_parse_avro.py similarity index 100% rename from tests/test_parse_avro_eager.py rename to tests/test_parse_avro.py diff --git a/tests/test_pcap_eager.py b/tests/test_pcap.py similarity index 100% rename from tests/test_pcap_eager.py rename to tests/test_pcap.py diff --git a/tests/test_pulsar_eager.py b/tests/test_pulsar.py similarity index 100% rename from tests/test_pulsar_eager.py rename to tests/test_pulsar.py diff --git a/tests/test_s3_eager.py b/tests/test_s3.py similarity index 100% rename from tests/test_s3_eager.py rename to tests/test_s3.py diff --git a/tests/test_serialization_eager.py b/tests/test_serialization.py similarity index 100% rename from tests/test_serialization_eager.py rename to tests/test_serialization.py diff --git a/tests/test_text_eager.py b/tests/test_text.py similarity index 100% rename from tests/test_text_eager.py rename to tests/test_text.py diff --git a/tests/test_version_eager.py b/tests/test_version.py similarity index 100% rename from tests/test_version_eager.py rename to tests/test_version.py diff --git a/tests/test_video_eager.py b/tests/test_video.py similarity index 100% rename from tests/test_video_eager.py rename to tests/test_video.py From 7010a48ce59e193c984cfa0a70cb48865169c0ac Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Wed, 3 Feb 2021 19:05:44 -0800 Subject: [PATCH 56/85] Combine Ubuntu 20.04 and CentOS 7 tests into one GitHub jobs (#1299) When GitHub Actions runs it looks like there is an implicit concurrent jobs limit. As such the CentOS 7 test normally is scheduled later after other jobs completes. However, many times CentOS 7 test hangs (e.g., https://github.com/tensorflow/io/runs/1825943449). This is likely due to the CentOS 7 test is on the GitHub Actions queue for too long. This PR moves CentOS 7 to run after Ubuntu 20.04 test complete, to try to avoid hangs. Signed-off-by: Yong Tang --- .github/workflows/build.yml | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 74128276b..2e1cc731d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -68,12 +68,13 @@ jobs: bash -x -e source.sh python3 -c 'import tensorflow as tf; print(tf.version.VERSION)' - ubuntu-2004: - name: Ubuntu 20.04 + linux: + name: Linux runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - run: | + - name: Ubuntu 20.04 + run: | if [[ "${EVENT_NAME}" == "push" && "${REPO_NAME}" == "tensorflow/io" ]]; then printf '%s\n' "${GCP_CREDS}" >service_account_creds.json export BAZEL_OPTIMIZATION="--remote_cache=https://storage.googleapis.com/tensorflow-sigs-io --remote_upload_local_results=true --google_credentials=service_account_creds.json" @@ -87,13 +88,8 @@ jobs: docker run -i --rm -v $PWD:/v -w /v --net=host \ -e BAZEL_OPTIMIZATION="${BAZEL_OPTIMIZATION}" \ ubuntu:20.04 bash -x -e source.sh - - centos-7: - name: CentOS 7 - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - run: | + - name: CentOS 7 + run: | if [[ "${EVENT_NAME}" == "push" && "${REPO_NAME}" == "tensorflow/io" ]]; then printf '%s\n' "${GCP_CREDS}" >service_account_creds.json export BAZEL_OPTIMIZATION="--remote_cache=https://storage.googleapis.com/tensorflow-sigs-io --remote_upload_local_results=true --google_credentials=service_account_creds.json" From 5091b94e5852be8d74798e00829111cf58ca5f56 Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Thu, 4 Feb 2021 11:25:40 -0800 Subject: [PATCH 57/85] Update names of api tests (#1300) We renamed the tests to remove "_eager" parts. This PR updates the api test for correct filenames Signed-off-by: Yong Tang --- .github/workflows/api.yml | 14 +++++++------- .github/workflows/benchmarks.yml | 6 +++--- .github/workflows/build.wheel.sh | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/api.yml b/.github/workflows/api.yml index 145d30884..66a8e8d31 100644 --- a/.github/workflows/api.yml +++ b/.github/workflows/api.yml @@ -45,10 +45,10 @@ jobs: python -m pip freeze python -c 'import tensorflow as tf; print(tf.version.VERSION)' python -c 'import tensorflow_io as tfio; print(tfio.version.VERSION)' - python -m pytest -s -v tests/test_http_eager.py - python -m pytest -s -v tests/test_s3_eager.py + python -m pytest -s -v tests/test_http.py + python -m pytest -s -v tests/test_s3.py python -m pytest -s -v tests/test_azure.py - python -m pytest -s -v tests/test_gcs_eager.py + python -m pytest -s -v tests/test_gcs.py linux: name: Linux ${{ matrix.python }} + ${{ matrix.version }} @@ -82,10 +82,10 @@ jobs: python -m pip freeze python -c 'import tensorflow as tf; print(tf.version.VERSION)' python -c 'import tensorflow_io as tfio; print(tfio.version.VERSION)' - python -m pytest -s -v tests/test_http_eager.py - python -m pytest -s -v tests/test_s3_eager.py + python -m pytest -s -v tests/test_http.py + python -m pytest -s -v tests/test_s3.py python -m pytest -s -v tests/test_azure.py - if [[ "${{ matrix.version }}" != "tf-nightly:tensorflow-io==0.17.0" ]]; then python -m pytest -s -v tests/test_gcs_eager.py ; fi + if [[ "${{ matrix.version }}" != "tf-nightly:tensorflow-io==0.17.0" ]]; then python -m pytest -s -v tests/test_gcs.py ; fi windows: name: Windows ${{ matrix.python }} + ${{ matrix.version }} @@ -120,4 +120,4 @@ jobs: python -m pip freeze python -c 'import tensorflow as tf; print(tf.version.VERSION)' python -c 'import tensorflow_io as tfio; print(tfio.version.VERSION)' - python -m pytest -s -v tests/test_http_eager.py -k remote + python -m pytest -s -v tests/test_http.py -k remote diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 67619432d..e94f432c9 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -36,7 +36,7 @@ jobs: python -m pip freeze python -c 'import tensorflow as tf; print(tf.version.VERSION)' python -c 'import tensorflow_io as tfio; print(tfio.version.VERSION)' - python -m pytest --benchmark-only -v --import-mode=append $(find . -type f \( -iname "test_*_eager.py" ! \( -iname "test_bigquery_eager.py" \) \)) + python -m pytest --benchmark-only -v --import-mode=append $(find . -type f \( -iname "test_*_v1.py" ! \( -iname "test_bigquery.py" \) \)) linux: name: Linux ${{ matrix.python }} + ${{ matrix.version }} @@ -70,7 +70,7 @@ jobs: python -m pip freeze python -c 'import tensorflow as tf; print(tf.version.VERSION)' python -c 'import tensorflow_io as tfio; print(tfio.version.VERSION)' - python -m pytest --benchmark-only --benchmark-json benchmark.json -v --import-mode=append $(find . -type f \( -iname "test_*_eager.py" ! \( -iname "test_bigquery_eager.py" \) \)) + python -m pytest --benchmark-only --benchmark-json benchmark.json -v --import-mode=append $(find . -type f \( -iname "test_*_v1.py" ! \( -iname "test_bigquery.py" \) \)) - name: Store benchmark result uses: rhysd/github-action-benchmark@v1 with: @@ -78,4 +78,4 @@ jobs: tool: 'pytest' output-file-path: benchmark.json github-token: ${{ secrets.GITHUB_TOKEN }} - auto-push: true \ No newline at end of file + auto-push: true diff --git a/.github/workflows/build.wheel.sh b/.github/workflows/build.wheel.sh index afb382cbb..cdcae0a9c 100755 --- a/.github/workflows/build.wheel.sh +++ b/.github/workflows/build.wheel.sh @@ -9,7 +9,7 @@ run_test() { $entry -m pip install -q pytest pytest-benchmark boto3 fastavro avro-python3 scikit-image pandas pyarrow==3.0.0 google-cloud-pubsub==2.1.0 google-cloud-bigtable==1.6.0 google-cloud-bigquery-storage==1.1.0 google-cloud-bigquery==2.3.1 google-cloud-storage==1.32.0 (cd tests && $entry -m pytest --benchmark-disable -v --import-mode=append $(find . -type f \( -iname "test_*_v1.py" \))) (cd tests && $entry -m pytest --benchmark-disable -v --import-mode=append $(find . -type f \( -iname "test_*.py" ! \( -iname "test_*_v1.py" -o -iname "test_bigquery.py" \) \))) - # GRPC and test_bigquery_eager tests have to be executed separately because of https://github.com/grpc/grpc/issues/20034 + # GRPC and test_bigquery tests have to be executed separately because of https://github.com/grpc/grpc/issues/20034 (cd tests && $entry -m pytest --benchmark-disable -v --import-mode=append $(find . -type f \( -iname "test_bigquery.py" \))) } From 79ccf5e4f2f3eeef3df3b9535663363acd6d9128 Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Thu, 4 Feb 2021 12:54:30 -0800 Subject: [PATCH 58/85] Fix wrong benchmark tests names (#1301) Fixes wrong benchmark tests names caused by last commit Signed-off-by: Yong Tang --- .github/workflows/benchmarks.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index e94f432c9..68fa2255f 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -36,7 +36,7 @@ jobs: python -m pip freeze python -c 'import tensorflow as tf; print(tf.version.VERSION)' python -c 'import tensorflow_io as tfio; print(tfio.version.VERSION)' - python -m pytest --benchmark-only -v --import-mode=append $(find . -type f \( -iname "test_*_v1.py" ! \( -iname "test_bigquery.py" \) \)) + python -m pytest --benchmark-only -v --import-mode=append $(find . -type f \( -iname "test_*.py" ! \( -iname "test_*_v1.py" -o -iname "test_bigquery.py" \) \)) linux: name: Linux ${{ matrix.python }} + ${{ matrix.version }} @@ -70,7 +70,7 @@ jobs: python -m pip freeze python -c 'import tensorflow as tf; print(tf.version.VERSION)' python -c 'import tensorflow_io as tfio; print(tfio.version.VERSION)' - python -m pytest --benchmark-only --benchmark-json benchmark.json -v --import-mode=append $(find . -type f \( -iname "test_*_v1.py" ! \( -iname "test_bigquery.py" \) \)) + python -m pytest --benchmark-only --benchmark-json benchmark.json -v --import-mode=append $(find . -type f \( -iname "test_*.py" ! \( -iname "test_*_v1.py" -o -iname "test_bigquery.py" \) \)) - name: Store benchmark result uses: rhysd/github-action-benchmark@v1 with: From 7945ec59960fe352e4259b580758bd2d48029acc Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Sun, 7 Feb 2021 18:08:19 -0800 Subject: [PATCH 59/85] Patch arrow to temporarily resolve the ARROW-11518 issue (#1304) This PR patchs arrow to temporarily resolve the ARROW-11518 issue. See 1281 for details Credit to diggerk. We will update arrow after the upstream PR is merged. Signed-off-by: Yong Tang --- WORKSPACE | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/WORKSPACE b/WORKSPACE index 3475df9e7..58cc1f597 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -267,6 +267,11 @@ http_archive( http_archive( name = "arrow", build_file = "//third_party:arrow.BUILD", + patch_cmds = [ + # TODO: Remove the fowllowing once arrow issue is resolved. + """sed -i.bak 's/type_traits/std::max(sizeof(int16_t), type_traits/g' cpp/src/parquet/column_reader.cc""", + """sed -i.bak 's/value_byte_size/value_byte_size)/g' cpp/src/parquet/column_reader.cc""", + ], sha256 = "fc461c4f0a60e7470a7c58b28e9344aa8fb0be5cc982e9658970217e084c3a82", strip_prefix = "arrow-apache-arrow-3.0.0", urls = [ From a398d269046add693cac80adcadad809939e7a02 Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Mon, 8 Feb 2021 07:37:46 -0800 Subject: [PATCH 60/85] Avoid error if plugins .so module is not available (#1302) This PR raises a warning instead of an error in case plugins .so module is not available, so that tensorflow-io package can be at least partially used with python-only functions. Signed-off-by: Yong Tang --- tensorflow_io/core/python/ops/__init__.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tensorflow_io/core/python/ops/__init__.py b/tensorflow_io/core/python/ops/__init__.py index c9f6865a9..ebb0cfd47 100644 --- a/tensorflow_io/core/python/ops/__init__.py +++ b/tensorflow_io/core/python/ops/__init__.py @@ -18,6 +18,7 @@ import ctypes import sys import inspect +import warnings import types import tensorflow as tf @@ -95,5 +96,8 @@ def __dir__(self): plugin_ops = _load_library("libtensorflow_io_plugins.so", "fs") except NotImplementedError as e: # Note: load libtensorflow_io.so imperatively in case of statically linking - core_ops = _load_library("libtensorflow_io.so") - plugin_ops = _load_library("libtensorflow_io.so", "fs") + try: + core_ops = _load_library("libtensorflow_io.so") + plugin_ops = _load_library("libtensorflow_io.so", "fs") + except NotImplementedError as e: + warnings.warn("file system plugins are not loaded: {}".format(e)) From df04e372bb3f2c777efa729a98c8f71d98f075a4 Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Mon, 8 Feb 2021 08:15:20 -0800 Subject: [PATCH 61/85] =?UTF-8?q?Remove=20AWS=20headers=20from=20tensorflo?= =?UTF-8?q?w,=20and=20use=20headers=20from=20third=5Fparty=20=E2=80=A6=20(?= =?UTF-8?q?#1241)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Remove external headers from tensorflow, and use third_party headers instead This PR removes external headers from tensorflow, and use third_party headers instead. Signed-off-by: Yong Tang * Address review comment Signed-off-by: Yong Tang --- third_party/toolchains/tf/BUILD.tpl | 7 +++++++ third_party/toolchains/tf/tf_configure.bzl | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/third_party/toolchains/tf/BUILD.tpl b/third_party/toolchains/tf/BUILD.tpl index 80a428bc3..85c1cad0d 100644 --- a/third_party/toolchains/tf/BUILD.tpl +++ b/third_party/toolchains/tf/BUILD.tpl @@ -4,6 +4,13 @@ cc_library( name = "tf_header_lib", hdrs = [":tf_header_include"], includes = ["include"], + deps = [ + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/container:inlined_vector", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/types:optional", + "@com_google_absl//absl/types:span", + ], visibility = ["//visibility:public"], ) diff --git a/third_party/toolchains/tf/tf_configure.bzl b/third_party/toolchains/tf/tf_configure.bzl index b898511bf..3565f078b 100644 --- a/third_party/toolchains/tf/tf_configure.bzl +++ b/third_party/toolchains/tf/tf_configure.bzl @@ -147,7 +147,7 @@ def _symlink_genrule_for_dir( if src_dir != None: src_dir = _norm_path(src_dir) dest_dir = _norm_path(dest_dir) - files = "\n".join(sorted(_read_dir(repository_ctx, src_dir).splitlines())) + files = "\n".join(sorted([e for e in _read_dir(repository_ctx, src_dir).splitlines() if ("/external/" not in e) and ("/absl/" not in e)])) # Create a list with the src_dir stripped to use for outputs. if tf_pip_dir_rename_pair_len: From cc93afae251ebd1d89ce7d0ab9288005e6d18531 Mon Sep 17 00:00:00 2001 From: Mark Daoust Date: Mon, 8 Feb 2021 09:15:14 -0800 Subject: [PATCH 62/85] Fix docstring. (#1305) This is breaking everything below it. https://www.tensorflow.org/io/api_docs/python/tfio/experimental/IODataset --- tensorflow_io/core/python/experimental/io_dataset_ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow_io/core/python/experimental/io_dataset_ops.py b/tensorflow_io/core/python/experimental/io_dataset_ops.py index 8ff46c22c..2367c7668 100644 --- a/tensorflow_io/core/python/experimental/io_dataset_ops.py +++ b/tensorflow_io/core/python/experimental/io_dataset_ops.py @@ -171,7 +171,7 @@ def from_numpy_file(cls, filename, spec=None, **kwargs): In case numpy file consists of unnamed elements, a tuple of numpy arrays are returned, otherwise a dict is returned for named elements. - ``` + Args: filename: filename of numpy file (npy or npz). spec: A tuple of tf.TensorSpec or dtype, or a dict of From f34d1937373834f6421d434bcb591ec033ac1c71 Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Mon, 8 Feb 2021 23:07:34 -0800 Subject: [PATCH 63/85] Switch to use github to download libgeotiff (#1307) Signed-off-by: Yong Tang --- WORKSPACE | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/WORKSPACE b/WORKSPACE index 58cc1f597..12238181b 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -145,8 +145,8 @@ http_archive( sha256 = "9452dadd126223a22ce6b97d202066d3873792aaefa7ce739519635a3fe34034", strip_prefix = "libgeotiff-1.6.0", urls = [ - "https://storage.googleapis.com/mirror.tensorflow.org/download.osgeo.org/geotiff/libgeotiff/libgeotiff-1.6.0.zip", - "https://download.osgeo.org/geotiff/libgeotiff/libgeotiff-1.6.0.zip", + "https://storage.googleapis.com/mirror.tensorflow.org/github.com/OSGeo/libgeotiff/releases/download/1.6.0/libgeotiff-1.6.0.zip", + "https://github.com/OSGeo/libgeotiff/releases/download/1.6.0/libgeotiff-1.6.0.zip", ], ) @@ -156,8 +156,8 @@ http_archive( sha256 = "219c6e11b2baa9a3e2bd7ec54ce19702909591032cf6f7d1004b406f10b7c9ad", strip_prefix = "proj-7.2.1", urls = [ - "https://storage.googleapis.com/mirror.tensorflow.org/download.osgeo.org/proj/proj-7.2.1.zip", - "https://download.osgeo.org/proj/proj-7.2.1.zip", + "https://storage.googleapis.com/mirror.tensorflow.org/github.com/OSGeo/PROJ/releases/download/7.2.1/proj-7.2.1.zip", + "https://github.com/OSGeo/PROJ/releases/download/7.2.1/proj-7.2.1.zip", ], ) From 801569ff1a626354ebf7aa4d09270b8eefc3b46f Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Tue, 9 Feb 2021 11:04:01 -0800 Subject: [PATCH 64/85] Add @com_google_absl//absl/strings:cord (#1308) Fix read/STDIN_FILENO Signed-off-by: Yong Tang --- tensorflow_io/core/kernels/text_kernels.cc | 3 ++- tensorflow_io/core/kernels/video_kernels.h | 1 + third_party/toolchains/tf/BUILD.tpl | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/tensorflow_io/core/kernels/text_kernels.cc b/tensorflow_io/core/kernels/text_kernels.cc index 119437705..dea82134f 100644 --- a/tensorflow_io/core/kernels/text_kernels.cc +++ b/tensorflow_io/core/kernels/text_kernels.cc @@ -16,10 +16,11 @@ limitations under the License. #include "tensorflow/core/framework/op_kernel.h" #include "tensorflow/core/lib/io/buffered_inputstream.h" #include "tensorflow_io/core/kernels/io_stream.h" - #if defined(_MSC_VER) #include #define STDIN_FILENO _fileno(stdin) +#else +#include #endif namespace tensorflow { diff --git a/tensorflow_io/core/kernels/video_kernels.h b/tensorflow_io/core/kernels/video_kernels.h index cddcfe594..91838dbe5 100644 --- a/tensorflow_io/core/kernels/video_kernels.h +++ b/tensorflow_io/core/kernels/video_kernels.h @@ -23,6 +23,7 @@ limitations under the License. #include #include #include +#include static int xioctl(int fh, int request, void* arg) { int r; diff --git a/third_party/toolchains/tf/BUILD.tpl b/third_party/toolchains/tf/BUILD.tpl index 85c1cad0d..425a96e15 100644 --- a/third_party/toolchains/tf/BUILD.tpl +++ b/third_party/toolchains/tf/BUILD.tpl @@ -8,6 +8,7 @@ cc_library( "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/container:inlined_vector", "@com_google_absl//absl/strings", + "@com_google_absl//absl/strings:cord", "@com_google_absl//absl/types:optional", "@com_google_absl//absl/types:span", ], From a871e52d4f75dbfe6837e55a3865d5b135f7d379 Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Thu, 11 Feb 2021 14:42:26 -0800 Subject: [PATCH 65/85] Switch to modular file system for hdfs (#1309) * Switch to modular file system for hdfs This PR is part of the effort to switch to modular file system for hdfs. When TF_ENABLE_LEGACY_FILESYSTEM=1 is provided, old behavior will be preserved. Signed-off-by: Yong Tang * Build against tf-nightly Signed-off-by: Yong Tang * Update tests Signed-off-by: Yong Tang * Adjust the if else logic, follow review comment Signed-off-by: Yong Tang --- .../core/plugins/file_system_plugins.cc | 18 +++++++++++++++--- tensorflow_io/core/python/ops/version_ops.py | 4 ++-- tests/test_hdfs.py | 4 ++-- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/tensorflow_io/core/plugins/file_system_plugins.cc b/tensorflow_io/core/plugins/file_system_plugins.cc index bc408dcb7..447e8757a 100644 --- a/tensorflow_io/core/plugins/file_system_plugins.cc +++ b/tensorflow_io/core/plugins/file_system_plugins.cc @@ -15,7 +15,13 @@ limitations under the License. #include "tensorflow_io/core/plugins/file_system_plugins.h" +#include "absl/strings/ascii.h" + void TF_InitPlugin(TF_FilesystemPluginInfo* info) { + const char* enable_legacy_env = getenv("TF_ENABLE_LEGACY_FILESYSTEM"); + std::string enable_legacy = + enable_legacy_env ? absl::AsciiStrToLower(enable_legacy_env) : ""; + info->plugin_memory_allocate = tensorflow::io::plugin_memory_allocate; info->plugin_memory_free = tensorflow::io::plugin_memory_free; info->num_schemes = 7; @@ -25,8 +31,14 @@ void TF_InitPlugin(TF_FilesystemPluginInfo* info) { tensorflow::io::az::ProvideFilesystemSupportFor(&info->ops[0], "az"); tensorflow::io::http::ProvideFilesystemSupportFor(&info->ops[1], "http"); tensorflow::io::s3::ProvideFilesystemSupportFor(&info->ops[2], "s3e"); - tensorflow::io::hdfs::ProvideFilesystemSupportFor(&info->ops[3], "hdfse"); - tensorflow::io::hdfs::ProvideFilesystemSupportFor(&info->ops[4], "viewfse"); - tensorflow::io::hdfs::ProvideFilesystemSupportFor(&info->ops[5], "hare"); + if (enable_legacy == "true" || enable_legacy == "1") { + tensorflow::io::hdfs::ProvideFilesystemSupportFor(&info->ops[3], "hdfse"); + tensorflow::io::hdfs::ProvideFilesystemSupportFor(&info->ops[4], "viewfse"); + tensorflow::io::hdfs::ProvideFilesystemSupportFor(&info->ops[5], "hare"); + } else { + tensorflow::io::hdfs::ProvideFilesystemSupportFor(&info->ops[3], "hdfs"); + tensorflow::io::hdfs::ProvideFilesystemSupportFor(&info->ops[4], "viewfs"); + tensorflow::io::hdfs::ProvideFilesystemSupportFor(&info->ops[5], "har"); + } tensorflow::io::gs::ProvideFilesystemSupportFor(&info->ops[6], "gse"); } diff --git a/tensorflow_io/core/python/ops/version_ops.py b/tensorflow_io/core/python/ops/version_ops.py index d6d6e3122..e61f2470d 100644 --- a/tensorflow_io/core/python/ops/version_ops.py +++ b/tensorflow_io/core/python/ops/version_ops.py @@ -14,5 +14,5 @@ # ============================================================================== """version_ops""" -package = "tensorflow>=2.4.0,<2.5.0" -version = "0.17.0" +package = "tf-nightly" +version = "0.18.0" diff --git a/tests/test_hdfs.py b/tests/test_hdfs.py index 468d04b06..1324a597d 100644 --- a/tests/test_hdfs.py +++ b/tests/test_hdfs.py @@ -35,8 +35,8 @@ def test_read_file(): print("ADDRESS: {}".format(address)) body = b"1234567" - tf.io.write_file("hdfse://{}:9000/file.txt".format(address), body) + tf.io.write_file("hdfs://{}:9000/file.txt".format(address), body) - content = tf.io.read_file("hdfse://{}:9000/file.txt".format(address)) + content = tf.io.read_file("hdfs://{}:9000/file.txt".format(address)) print("CONTENT: {}".format(content)) assert content == body From 5b77f967c1c4349efa8d5453f5706dcd9d44fe80 Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Thu, 11 Feb 2021 22:27:59 -0800 Subject: [PATCH 66/85] Disable test_write_kafka test for now. (#1310) With tensorflow upgrade to tf-nightly, the test_write_kafka test is failing and that is block the plan to modular file system migration. This PR disables the test temporarily so that CI can continue to push tensorflow-io-nightly image (needed for modular file system migration) Signed-off-by: Yong Tang --- tests/test_kafka_v1.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_kafka_v1.py b/tests/test_kafka_v1.py index 8c539473c..74be1a8ab 100644 --- a/tests/test_kafka_v1.py +++ b/tests/test_kafka_v1.py @@ -237,6 +237,7 @@ def test_kafka_wrong_topic_configuration_failed(self): with self.assertRaises(errors.InternalError): sess.run(get_next) + @pytest.mark.skip(reason="TODO") def test_write_kafka(self): """test_write_kafka""" channel = "e{}e".format(time.time()) From 53c9a71c1346392221c671395746e4eec9a5b02d Mon Sep 17 00:00:00 2001 From: Vignesh Kothapalli Date: Sun, 14 Feb 2021 00:30:08 +0530 Subject: [PATCH 67/85] Modify --plat-name for macosx wheels (#1311) * modify --plat-name for macosx wheels * switch to 10.14 --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2e1cc731d..64430bc50 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -162,7 +162,7 @@ jobs: set -x -e python -m pip install -U wheel setuptools python --version - python setup.py --data bazel-bin -q bdist_wheel --plat-name macosx_10_13_x86_64 + python setup.py --data bazel-bin -q bdist_wheel --plat-name macosx_10_14_x86_64 - name: Auditwheel ${{ matrix.python }} macOS run: | set -x -e @@ -565,7 +565,7 @@ jobs: set -x -e python -m pip install -U wheel setuptools python --version - python setup.py --data bazel-bin -q bdist_wheel --plat-name macosx_10_13_x86_64 --nightly $BUILD_NUMBER + python setup.py --data bazel-bin -q bdist_wheel --plat-name macosx_10_14_x86_64 --nightly $BUILD_NUMBER - name: Auditwheel ${{ matrix.python }} macOS run: | set -x -e From 3f7f2920101825a2acdeee5738ab9813ada1becb Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Sat, 13 Feb 2021 13:39:38 -0800 Subject: [PATCH 68/85] Switch to modular file system for s3 (#1312) This PR is part of the effort to switch to modular file system for s3. When TF_ENABLE_LEGACY_FILESYSTEM=1 is provided, old behavior will be preserved. Signed-off-by: Yong Tang --- tensorflow_io/core/plugins/file_system_plugins.cc | 15 ++++++++++++++- tests/test_s3.py | 4 +--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/tensorflow_io/core/plugins/file_system_plugins.cc b/tensorflow_io/core/plugins/file_system_plugins.cc index 447e8757a..347018d7d 100644 --- a/tensorflow_io/core/plugins/file_system_plugins.cc +++ b/tensorflow_io/core/plugins/file_system_plugins.cc @@ -30,12 +30,25 @@ void TF_InitPlugin(TF_FilesystemPluginInfo* info) { sizeof(info->ops[0]))); tensorflow::io::az::ProvideFilesystemSupportFor(&info->ops[0], "az"); tensorflow::io::http::ProvideFilesystemSupportFor(&info->ops[1], "http"); - tensorflow::io::s3::ProvideFilesystemSupportFor(&info->ops[2], "s3e"); if (enable_legacy == "true" || enable_legacy == "1") { +// TODO: enable on windows once tf-nightly releases windows build +// that contains TF_ENABLE_LEGACY_FILESYSTEM. +#if defined(_MSC_VER) + tensorflow::io::s3::ProvideFilesystemSupportFor(&info->ops[2], "s3"); +#else + tensorflow::io::s3::ProvideFilesystemSupportFor(&info->ops[2], "s3e"); +#endif tensorflow::io::hdfs::ProvideFilesystemSupportFor(&info->ops[3], "hdfse"); tensorflow::io::hdfs::ProvideFilesystemSupportFor(&info->ops[4], "viewfse"); tensorflow::io::hdfs::ProvideFilesystemSupportFor(&info->ops[5], "hare"); } else { +// TODO: enable on windows once tf-nightly releases windows build +// that contains TF_ENABLE_LEGACY_FILESYSTEM. +#if defined(_MSC_VER) + tensorflow::io::s3::ProvideFilesystemSupportFor(&info->ops[2], "s3e"); +#else + tensorflow::io::s3::ProvideFilesystemSupportFor(&info->ops[2], "s3"); +#endif tensorflow::io::hdfs::ProvideFilesystemSupportFor(&info->ops[3], "hdfs"); tensorflow::io::hdfs::ProvideFilesystemSupportFor(&info->ops[4], "viewfs"); tensorflow::io::hdfs::ProvideFilesystemSupportFor(&info->ops[5], "har"); diff --git a/tests/test_s3.py b/tests/test_s3.py index ad18af1f7..6f008f3a9 100644 --- a/tests/test_s3.py +++ b/tests/test_s3.py @@ -55,7 +55,5 @@ def test_read_file(): os.environ["S3_USE_HTTPS"] = "0" os.environ["S3_VERIFY_SSL"] = "0" - # TODO: The following is not working yet, need update to use - # s3 implementation with module file system - content = tf.io.read_file("s3e://{}/{}".format(bucket_name, key_name)) + content = tf.io.read_file("s3://{}/{}".format(bucket_name, key_name)) assert content == body From 33fca5669a2256d17c97de49d43e093982994c88 Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Fri, 26 Feb 2021 12:10:51 -0800 Subject: [PATCH 69/85] Update to enable python 3.9 building on Linux (#1314) * Update to enable python 3.9 building on Linux Signed-off-by: Yong Tang * Switch to always use ubuntu:20.04 Signed-off-by: Yong Tang --- .github/workflows/build.yml | 29 ++++++++++++++++------------- setup.py | 2 +- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 64430bc50..49a5d855a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -252,7 +252,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python: ['3.6', '3.7', '3.8'] + python: ['3.6', '3.7', '3.8', '3.9'] steps: - uses: actions/checkout@v2 - uses: actions/download-artifact@v1 @@ -281,16 +281,10 @@ jobs: linux-test: name: Test ${{ matrix.python }} Linux needs: linux-wheel - runs-on: ${{ matrix.os }} + runs-on: ubuntu-20.04 strategy: matrix: - os: [ubuntu-18.04, ubuntu-20.04] - python: ['3.7', '3.8'] - exclude: - - os: ubuntu-18.04 - python: '3.8' - - os: ubuntu-20.04 - python: '3.7' + python: ['3.7', '3.8', '3.9'] steps: - uses: actions/checkout@v2 - uses: actions/download-artifact@v1 @@ -317,7 +311,7 @@ jobs: set -x -e df -h docker run -i --rm -v $PWD:/v -w /v --net=host \ - buildpack-deps:$(echo ${{ matrix.os }} | awk -F- '{print $2}') \ + buildpack-deps:20.04 \ bash -x -e .github/workflows/build.wheel.sh python${{ matrix.python }} windows-bazel: @@ -469,6 +463,10 @@ jobs: with: name: Linux-3.8-wheel path: Linux-3.8-wheel + - uses: actions/download-artifact@v1 + with: + name: Linux-3.9-wheel + path: Linux-3.9-wheel - uses: actions/download-artifact@v1 with: name: Windows-3.6-wheel @@ -490,6 +488,7 @@ jobs: cp Linux-3.6-wheel/*.whl wheelhouse/ cp Linux-3.7-wheel/*.whl wheelhouse/ cp Linux-3.8-wheel/*.whl wheelhouse/ + cp Linux-3.9-wheel/*.whl wheelhouse/ cp Windows-3.6-wheel/*.whl wheelhouse/ cp Windows-3.7-wheel/*.whl wheelhouse/ cp Windows-3.8-wheel/*.whl wheelhouse/ @@ -585,11 +584,10 @@ jobs: name: Nightly ${{ matrix.python }} Linux if: github.event_name == 'push' needs: [build-number, release] - runs-on: ${{ matrix.os }} + runs-on: ubuntu-20.04 strategy: matrix: - os: [ubuntu-18.04] - python: ['3.6', '3.7', '3.8'] + python: ['3.6', '3.7', '3.8', '3.9'] steps: - uses: actions/download-artifact@v1 with: @@ -685,6 +683,10 @@ jobs: with: name: Linux-3.8-nightly path: Linux-3.8-nightly + - uses: actions/download-artifact@v1 + with: + name: Linux-3.9-nightly + path: Linux-3.9-nightly - uses: actions/download-artifact@v1 with: name: Windows-3.6-nightly @@ -706,6 +708,7 @@ jobs: cp Linux-3.6-nightly/*.whl dist/ cp Linux-3.7-nightly/*.whl dist/ cp Linux-3.8-nightly/*.whl dist/ + cp Linux-3.9-nightly/*.whl dist/ cp Windows-3.6-nightly/*.whl dist/ cp Windows-3.7-nightly/*.whl dist/ cp Windows-3.8-nightly/*.whl dist/ diff --git a/setup.py b/setup.py index 74a891891..8e7835c4f 100644 --- a/setup.py +++ b/setup.py @@ -134,7 +134,7 @@ def has_ext_modules(self): ], keywords="tensorflow io machine learning", packages=setuptools.find_packages(where=".", exclude=["tests"]), - python_requires=">=3.5, <3.9", + python_requires=">=3.5, <3.10", install_requires=[package], package_data={".": ["*.so"],}, project_urls={ From 314f406f47312fb5c6a69fbaa7c3557ce800c7a0 Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Sat, 27 Feb 2021 09:35:07 -0800 Subject: [PATCH 70/85] Add python 3.9 on Windows (#1316) --- .github/workflows/build.yml | 16 +++++++++++++--- .../core/plugins/file_system_plugins.cc | 12 ------------ 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 49a5d855a..015cfdd01 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -364,7 +364,7 @@ jobs: runs-on: windows-latest strategy: matrix: - python: ['3.6', '3.7', '3.8'] + python: ['3.6', '3.7', '3.8', '3.9'] steps: - uses: actions/checkout@v2 - uses: actions/download-artifact@v1 @@ -393,7 +393,7 @@ jobs: runs-on: windows-latest strategy: matrix: - python: ['3.7', '3.8'] + python: ['3.7', '3.8', '3.9'] steps: - uses: actions/checkout@v2 - uses: actions/download-artifact@v1 @@ -479,6 +479,10 @@ jobs: with: name: Windows-3.8-wheel path: Windows-3.8-wheel + - uses: actions/download-artifact@v1 + with: + name: Windows-3.9-wheel + path: Windows-3.9-wheel - run: | set -e -x mkdir -p wheelhouse @@ -492,6 +496,7 @@ jobs: cp Windows-3.6-wheel/*.whl wheelhouse/ cp Windows-3.7-wheel/*.whl wheelhouse/ cp Windows-3.8-wheel/*.whl wheelhouse/ + cp Windows-3.9-wheel/*.whl wheelhouse/ ls -la wheelhouse/ sha256sum wheelhouse/*.whl - uses: actions/upload-artifact@v1 @@ -625,7 +630,7 @@ jobs: runs-on: windows-latest strategy: matrix: - python: ['3.6', '3.7', 3.8] + python: ['3.6', '3.7', '3.8', '3.9'] steps: - uses: actions/download-artifact@v1 with: @@ -699,6 +704,10 @@ jobs: with: name: Windows-3.8-nightly path: Windows-3.8-nightly + - uses: actions/download-artifact@v1 + with: + name: Windows-3.9-nightly + path: Windows-3.9-nightly - run: | set -e -x mkdir -p dist @@ -712,6 +721,7 @@ jobs: cp Windows-3.6-nightly/*.whl dist/ cp Windows-3.7-nightly/*.whl dist/ cp Windows-3.8-nightly/*.whl dist/ + cp Windows-3.9-nightly/*.whl dist/ ls -la dist/ sha256sum dist/*.whl - uses: pypa/gh-action-pypi-publish@master diff --git a/tensorflow_io/core/plugins/file_system_plugins.cc b/tensorflow_io/core/plugins/file_system_plugins.cc index 347018d7d..5cf1bc4d9 100644 --- a/tensorflow_io/core/plugins/file_system_plugins.cc +++ b/tensorflow_io/core/plugins/file_system_plugins.cc @@ -31,24 +31,12 @@ void TF_InitPlugin(TF_FilesystemPluginInfo* info) { tensorflow::io::az::ProvideFilesystemSupportFor(&info->ops[0], "az"); tensorflow::io::http::ProvideFilesystemSupportFor(&info->ops[1], "http"); if (enable_legacy == "true" || enable_legacy == "1") { -// TODO: enable on windows once tf-nightly releases windows build -// that contains TF_ENABLE_LEGACY_FILESYSTEM. -#if defined(_MSC_VER) - tensorflow::io::s3::ProvideFilesystemSupportFor(&info->ops[2], "s3"); -#else tensorflow::io::s3::ProvideFilesystemSupportFor(&info->ops[2], "s3e"); -#endif tensorflow::io::hdfs::ProvideFilesystemSupportFor(&info->ops[3], "hdfse"); tensorflow::io::hdfs::ProvideFilesystemSupportFor(&info->ops[4], "viewfse"); tensorflow::io::hdfs::ProvideFilesystemSupportFor(&info->ops[5], "hare"); } else { -// TODO: enable on windows once tf-nightly releases windows build -// that contains TF_ENABLE_LEGACY_FILESYSTEM. -#if defined(_MSC_VER) - tensorflow::io::s3::ProvideFilesystemSupportFor(&info->ops[2], "s3e"); -#else tensorflow::io::s3::ProvideFilesystemSupportFor(&info->ops[2], "s3"); -#endif tensorflow::io::hdfs::ProvideFilesystemSupportFor(&info->ops[3], "hdfs"); tensorflow::io::hdfs::ProvideFilesystemSupportFor(&info->ops[4], "viewfs"); tensorflow::io::hdfs::ProvideFilesystemSupportFor(&info->ops[5], "har"); From fb5cab8d30f6832be861edcc437062a6c7ed89c0 Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Tue, 2 Mar 2021 04:33:49 -0800 Subject: [PATCH 71/85] Use `-p 9000:9000` (and hide 8088) when launch hadoop (#1317) --- tests/test_hdfs/hdfs_test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_hdfs/hdfs_test.sh b/tests/test_hdfs/hdfs_test.sh index 64eae6921..c46916332 100755 --- a/tests/test_hdfs/hdfs_test.sh +++ b/tests/test_hdfs/hdfs_test.sh @@ -19,7 +19,7 @@ set -o pipefail HADOOP_VERSION=2.7.0 docker pull sequenceiq/hadoop-docker:$HADOOP_VERSION -docker run -d --rm --net=host --name=tensorflow-io-hdfs sequenceiq/hadoop-docker:$HADOOP_VERSION +docker run -d --rm -p 9000:9000 --name=tensorflow-io-hdfs sequenceiq/hadoop-docker:$HADOOP_VERSION echo "Waiting for 30 secs until hadoop is up and running" sleep 30 docker logs tensorflow-io-hdfs From 221e2213215081565e78966bf8d717282959c170 Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Fri, 5 Mar 2021 09:08:19 -0800 Subject: [PATCH 72/85] Experimental: Add initial wavefront/obj parser for vertices (#1315) This PR is an early experimental implementation of wavefront obj parser in tensorflow-io for 3D objects. This PR is the first step to obtain raw vertices in float32 tensor with shape of `[n, 3]`. Additional follow up PRs will be needed to handle meshs with different shapes (not sure if ragged tensor will be a good fit in that case) Some background on obj file: Wavefront (obj) is a format widely used in 3D (another is ply) modeling (http://paulbourke.net/dataformats/obj/). It is simple (ASCII) with good support for many softwares. Machine learning in 3D has been an active field with some advances such as PolyGen (https://arxiv.org/abs/2002.10880) Processing obj files are needed to process 3D with tensorflow. In 3D the basic elements could be vertices or faces. This PR tries to cover vertices first so that vertices in obj file can be loaded into TF's graph for further processing within graph pipeline. Signed-off-by: Yong Tang --- WORKSPACE | 11 +++ tensorflow_io/core/BUILD | 17 +++++ tensorflow_io/core/kernels/obj_kernels.cc | 70 +++++++++++++++++++ tensorflow_io/core/ops/obj_ops.cc | 36 ++++++++++ .../core/python/api/experimental/image.py | 1 + .../core/python/experimental/image_ops.py | 15 ++++ tests/test_obj.py | 37 ++++++++++ tests/test_obj/sample.obj | 6 ++ third_party/tinyobjloader.BUILD | 14 ++++ 9 files changed, 207 insertions(+) create mode 100644 tensorflow_io/core/kernels/obj_kernels.cc create mode 100644 tensorflow_io/core/ops/obj_ops.cc create mode 100644 tests/test_obj.py create mode 100644 tests/test_obj/sample.obj create mode 100644 third_party/tinyobjloader.BUILD diff --git a/WORKSPACE b/WORKSPACE index 12238181b..d5f7ab095 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -1110,3 +1110,14 @@ http_archive( "https://github.com/mongodb/mongo-c-driver/releases/download/1.16.2/mongo-c-driver-1.16.2.tar.gz", ], ) + +http_archive( + name = "tinyobjloader", + build_file = "//third_party:tinyobjloader.BUILD", + sha256 = "b8c972dfbbcef33d55554e7c9031abe7040795b67778ad3660a50afa7df6ec56", + strip_prefix = "tinyobjloader-2.0.0rc8", + urls = [ + "https://storage.googleapis.com/mirror.tensorflow.org/github.com/tinyobjloader/tinyobjloader/archive/v2.0.0rc8.tar.gz", + "https://github.com/tinyobjloader/tinyobjloader/archive/v2.0.0rc8.tar.gz", + ], +) diff --git a/tensorflow_io/core/BUILD b/tensorflow_io/core/BUILD index 184af811d..f05d9329c 100644 --- a/tensorflow_io/core/BUILD +++ b/tensorflow_io/core/BUILD @@ -695,6 +695,22 @@ cc_library( alwayslink = 1, ) +cc_library( + name = "obj_ops", + srcs = [ + "kernels/obj_kernels.cc", + "ops/obj_ops.cc", + ], + copts = tf_io_copts(), + linkstatic = True, + deps = [ + "@local_config_tf//:libtensorflow_framework", + "@local_config_tf//:tf_header_lib", + "@tinyobjloader", + ], + alwayslink = 1, +) + cc_binary( name = "python/ops/libtensorflow_io.so", copts = tf_io_copts(), @@ -717,6 +733,7 @@ cc_binary( "//tensorflow_io/core:parquet_ops", "//tensorflow_io/core:pcap_ops", "//tensorflow_io/core:pulsar_ops", + "//tensorflow_io/core:obj_ops", "//tensorflow_io/core:operation_ops", "//tensorflow_io/core:pubsub_ops", "//tensorflow_io/core:serialization_ops", diff --git a/tensorflow_io/core/kernels/obj_kernels.cc b/tensorflow_io/core/kernels/obj_kernels.cc new file mode 100644 index 000000000..e619431e1 --- /dev/null +++ b/tensorflow_io/core/kernels/obj_kernels.cc @@ -0,0 +1,70 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +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. +==============================================================================*/ + +#include "tensorflow/core/framework/op_kernel.h" +#include "tensorflow/core/platform/logging.h" +#include "tiny_obj_loader.h" + +namespace tensorflow { +namespace io { +namespace { + +class DecodeObjOp : public OpKernel { + public: + explicit DecodeObjOp(OpKernelConstruction* context) : OpKernel(context) {} + + void Compute(OpKernelContext* context) override { + const Tensor* input_tensor; + OP_REQUIRES_OK(context, context->input("input", &input_tensor)); + OP_REQUIRES(context, TensorShapeUtils::IsScalar(input_tensor->shape()), + errors::InvalidArgument("input must be scalar, got shape ", + input_tensor->shape().DebugString())); + const tstring& input = input_tensor->scalar()(); + + tinyobj::ObjReader reader; + + if (!reader.ParseFromString(input.c_str(), "")) { + OP_REQUIRES( + context, false, + errors::Internal("Unable to read obj file: ", reader.Error())); + } + + if (!reader.Warning().empty()) { + LOG(WARNING) << "TinyObjReader: " << reader.Warning(); + } + + auto& attrib = reader.GetAttrib(); + + int64 count = attrib.vertices.size() / 3; + + Tensor* output_tensor = nullptr; + OP_REQUIRES_OK(context, context->allocate_output(0, TensorShape({count, 3}), + &output_tensor)); + // Loop over attrib.vertices: + for (int64 i = 0; i < count; i++) { + tinyobj::real_t x = attrib.vertices[i * 3 + 0]; + tinyobj::real_t y = attrib.vertices[i * 3 + 1]; + tinyobj::real_t z = attrib.vertices[i * 3 + 2]; + output_tensor->tensor()(i, 0) = x; + output_tensor->tensor()(i, 1) = y; + output_tensor->tensor()(i, 2) = z; + } + } +}; +REGISTER_KERNEL_BUILDER(Name("IO>DecodeObj").Device(DEVICE_CPU), DecodeObjOp); + +} // namespace +} // namespace io +} // namespace tensorflow diff --git a/tensorflow_io/core/ops/obj_ops.cc b/tensorflow_io/core/ops/obj_ops.cc new file mode 100644 index 000000000..e3c45653a --- /dev/null +++ b/tensorflow_io/core/ops/obj_ops.cc @@ -0,0 +1,36 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +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. +==============================================================================*/ + +#include "tensorflow/core/framework/common_shape_fns.h" +#include "tensorflow/core/framework/op.h" +#include "tensorflow/core/framework/shape_inference.h" + +namespace tensorflow { +namespace io { +namespace { + +REGISTER_OP("IO>DecodeObj") + .Input("input: string") + .Output("output: float32") + .SetShapeFn([](shape_inference::InferenceContext* c) { + shape_inference::ShapeHandle unused; + TF_RETURN_IF_ERROR(c->WithRank(c->input(0), 0, &unused)); + c->set_output(0, c->MakeShape({c->UnknownDim(), 3})); + return Status::OK(); + }); + +} // namespace +} // namespace io +} // namespace tensorflow diff --git a/tensorflow_io/core/python/api/experimental/image.py b/tensorflow_io/core/python/api/experimental/image.py index 0d9c07e74..643b7b9c0 100644 --- a/tensorflow_io/core/python/api/experimental/image.py +++ b/tensorflow_io/core/python/api/experimental/image.py @@ -27,4 +27,5 @@ decode_yuy2, decode_avif, decode_jp2, + decode_obj, ) diff --git a/tensorflow_io/core/python/experimental/image_ops.py b/tensorflow_io/core/python/experimental/image_ops.py index b399de102..ebde7e6ae 100644 --- a/tensorflow_io/core/python/experimental/image_ops.py +++ b/tensorflow_io/core/python/experimental/image_ops.py @@ -208,3 +208,18 @@ def decode_jp2(contents, dtype=tf.uint8, name=None): A `Tensor` of type `uint8` and shape of `[height, width, 3]` (RGB). """ return core_ops.io_decode_jpeg2k(contents, dtype=dtype, name=name) + + +def decode_obj(contents, name=None): + """ + Decode a Wavefront (obj) file into a float32 tensor. + + Args: + contents: A 0-dimensional Tensor of type string, i.e the + content of the Wavefront (.obj) file. + name: A name for the operation (optional). + + Returns: + A `Tensor` of type `float32` and shape of `[n, 3]` for vertices. + """ + return core_ops.io_decode_obj(contents, name=name) diff --git a/tests/test_obj.py b/tests/test_obj.py new file mode 100644 index 000000000..ff89ef2b8 --- /dev/null +++ b/tests/test_obj.py @@ -0,0 +1,37 @@ +# Copyright 2021 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +"""Test Wavefront OBJ""" + +import os +import numpy as np +import pytest + +import tensorflow as tf +import tensorflow_io as tfio + + +def test_decode_obj(): + """Test case for decode obj""" + filename = os.path.join( + os.path.dirname(os.path.abspath(__file__)), "test_obj", "sample.obj", + ) + filename = "file://" + filename + + obj = tfio.experimental.image.decode_obj(tf.io.read_file(filename)) + expected = np.array( + [[-0.5, 0.0, 0.4], [-0.5, 0.0, -0.8], [-0.5, 1.0, -0.8], [-0.5, 1.0, 0.4]], + dtype=np.float32, + ) + assert np.array_equal(obj, expected) diff --git a/tests/test_obj/sample.obj b/tests/test_obj/sample.obj new file mode 100644 index 000000000..da8b327ff --- /dev/null +++ b/tests/test_obj/sample.obj @@ -0,0 +1,6 @@ +# Simple Wavefront file +v -0.500000 0.000000 0.400000 +v -0.500000 0.000000 -0.800000 +v -0.500000 1.000000 -0.800000 +v -0.500000 1.000000 0.400000 +f -4 -3 -2 -1 diff --git a/third_party/tinyobjloader.BUILD b/third_party/tinyobjloader.BUILD new file mode 100644 index 000000000..0e9f74df4 --- /dev/null +++ b/third_party/tinyobjloader.BUILD @@ -0,0 +1,14 @@ +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) # MIT license + +cc_library( + name = "tinyobjloader", + srcs = [ + "tiny_obj_loader.cc", + ], + hdrs = [ + "tiny_obj_loader.h", + ], + copts = [], +) From 3b81b85fd17b86ebf363ed73ef00bdcd2b1f3fba Mon Sep 17 00:00:00 2001 From: Vo Van Nghia Date: Sun, 7 Mar 2021 17:01:26 +0100 Subject: [PATCH 73/85] update `protobuf` version to `3.11.4` to match tensorflow-nightly (#1320) --- WORKSPACE | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/WORKSPACE b/WORKSPACE index d5f7ab095..3760a7727 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -12,11 +12,14 @@ cuda_configure(name = "local_config_cuda") http_archive( name = "com_google_protobuf", - sha256 = "cfcba2df10feec52a84208693937c17a4b5df7775e1635c1e3baffc487b24c9b", - strip_prefix = "protobuf-3.9.2", + patch_cmds = [ + """sed -i.bak 's/@six\\/\\/:six/\\/\\/external:six/g' BUILD""", + ], + sha256 = "9748c0d90e54ea09e5e75fb7fac16edce15d2028d4356f32211cfa3c0e956564", + strip_prefix = "protobuf-3.11.4", urls = [ - "https://storage.googleapis.com/mirror.tensorflow.org/github.com/protocolbuffers/protobuf/archive/v3.9.2.zip", - "https://github.com/protocolbuffers/protobuf/archive/v3.9.2.zip", + "https://storage.googleapis.com/mirror.tensorflow.org/github.com/protocolbuffers/protobuf/archive/v3.11.4.zip", + "https://github.com/protocolbuffers/protobuf/archive/v3.11.4.zip", ], ) From 1c85b773829f59aad99f7f0810dc0cc92cea7802 Mon Sep 17 00:00:00 2001 From: Vo Van Nghia Date: Fri, 12 Mar 2021 09:49:13 +0100 Subject: [PATCH 74/85] Revert "update `protobuf` version to `3.11.4` to match tensorflow-nightly (#1320)" (#1323) This reverts commit 07d833fe256b7daa3bba3f4a7e6bc53e6c05fdee. --- WORKSPACE | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/WORKSPACE b/WORKSPACE index 3760a7727..d5f7ab095 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -12,14 +12,11 @@ cuda_configure(name = "local_config_cuda") http_archive( name = "com_google_protobuf", - patch_cmds = [ - """sed -i.bak 's/@six\\/\\/:six/\\/\\/external:six/g' BUILD""", - ], - sha256 = "9748c0d90e54ea09e5e75fb7fac16edce15d2028d4356f32211cfa3c0e956564", - strip_prefix = "protobuf-3.11.4", + sha256 = "cfcba2df10feec52a84208693937c17a4b5df7775e1635c1e3baffc487b24c9b", + strip_prefix = "protobuf-3.9.2", urls = [ - "https://storage.googleapis.com/mirror.tensorflow.org/github.com/protocolbuffers/protobuf/archive/v3.11.4.zip", - "https://github.com/protocolbuffers/protobuf/archive/v3.11.4.zip", + "https://storage.googleapis.com/mirror.tensorflow.org/github.com/protocolbuffers/protobuf/archive/v3.9.2.zip", + "https://github.com/protocolbuffers/protobuf/archive/v3.9.2.zip", ], ) From ef46f8cb00af23eb7cef76a1b0d21d2ee87d8baa Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Fri, 12 Mar 2021 22:24:06 -0800 Subject: [PATCH 75/85] Enable python 3.9 build on macOS (#1324) This PR enables python 3.9 build on macOS, as tf-nightly is available with macOS now. Signed-off-by: Yong Tang --- .github/workflows/build.yml | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 015cfdd01..f3e8c8cff 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -147,7 +147,7 @@ jobs: runs-on: macos-latest strategy: matrix: - python: ['3.6', '3.7', '3.8'] + python: ['3.6', '3.7', '3.8', '3.9'] steps: - uses: actions/checkout@v2 - uses: actions/download-artifact@v1 @@ -184,7 +184,7 @@ jobs: runs-on: macos-latest strategy: matrix: - python: ['3.7', '3.8'] + python: ['3.7', '3.8', '3.9'] steps: - uses: actions/checkout@v2 - uses: actions/download-artifact@v1 @@ -451,6 +451,10 @@ jobs: with: name: macOS-3.8-wheel path: macOS-3.8-wheel + - uses: actions/download-artifact@v1 + with: + name: macOS-3.9-wheel + path: macOS-3.9-wheel - uses: actions/download-artifact@v1 with: name: Linux-3.6-wheel @@ -489,6 +493,7 @@ jobs: cp macOS-3.6-wheel/*.whl wheelhouse/ cp macOS-3.7-wheel/*.whl wheelhouse/ cp macOS-3.8-wheel/*.whl wheelhouse/ + cp macOS-3.9-wheel/*.whl wheelhouse/ cp Linux-3.6-wheel/*.whl wheelhouse/ cp Linux-3.7-wheel/*.whl wheelhouse/ cp Linux-3.8-wheel/*.whl wheelhouse/ @@ -549,7 +554,7 @@ jobs: runs-on: macos-latest strategy: matrix: - python: ['3.6', '3.7', '3.8'] + python: ['3.6', '3.7', '3.8', '3.9'] steps: - uses: actions/download-artifact@v1 with: @@ -676,6 +681,10 @@ jobs: with: name: macOS-3.8-nightly path: macOS-3.8-nightly + - uses: actions/download-artifact@v1 + with: + name: macOS-3.9-nightly + path: macOS-3.9-nightly - uses: actions/download-artifact@v1 with: name: Linux-3.6-nightly @@ -714,6 +723,7 @@ jobs: cp macOS-3.6-nightly/*.whl dist/ cp macOS-3.7-nightly/*.whl dist/ cp macOS-3.8-nightly/*.whl dist/ + cp macOS-3.9-nightly/*.whl dist/ cp Linux-3.6-nightly/*.whl dist/ cp Linux-3.7-nightly/*.whl dist/ cp Linux-3.8-nightly/*.whl dist/ From 31213088be49493c265d04fb54dbf296499825fe Mon Sep 17 00:00:00 2001 From: Vo Van Nghia Date: Sun, 14 Mar 2021 13:16:39 +0100 Subject: [PATCH 76/85] switch mnist dataset mirror to a more reliable one (#1327) --- README.md | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 2f84dc764..82ab09ade 100644 --- a/README.md +++ b/README.md @@ -24,9 +24,11 @@ import tensorflow as tf import tensorflow_io as tfio # Read the MNIST data into the IODataset. +dataset_url = "http://storage.googleapis.com/cvdf-datasets/mnist/" d_train = tfio.IODataset.from_mnist( - 'http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz', - 'http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz') + dataset_url + "train-images-idx3-ubyte.gz", + dataset_url + "train-labels-idx1-ubyte.gz", +) # Shuffle the elements of the dataset. d_train = d_train.shuffle(buffer_size=1024) @@ -38,17 +40,19 @@ d_train = d_train.map(lambda x, y: (tf.image.convert_image_dtype(x, tf.float32), d_train = d_train.batch(32) # Build the model. -model = tf.keras.models.Sequential([ - tf.keras.layers.Flatten(input_shape=(28, 28)), - tf.keras.layers.Dense(512, activation=tf.nn.relu), - tf.keras.layers.Dropout(0.2), - tf.keras.layers.Dense(10, activation=tf.nn.softmax) -]) +model = tf.keras.models.Sequential( + [ + tf.keras.layers.Flatten(input_shape=(28, 28)), + tf.keras.layers.Dense(512, activation=tf.nn.relu), + tf.keras.layers.Dropout(0.2), + tf.keras.layers.Dense(10, activation=tf.nn.softmax), + ] +) # Compile the model. -model.compile(optimizer='adam', - loss='sparse_categorical_crossentropy', - metrics=['accuracy']) +model.compile( + optimizer="adam", loss="sparse_categorical_crossentropy", metrics=["accuracy"] +) # Fit the model. model.fit(d_train, epochs=5, steps_per_epoch=200) From 57d840ba0716aab5bbcc50ce53e30de8559f5746 Mon Sep 17 00:00:00 2001 From: Vignesh Kothapalli Date: Sun, 14 Mar 2021 23:32:38 +0530 Subject: [PATCH 77/85] remove flaky centos 7 based build action (#1328) --- .github/workflows/build.yml | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f3e8c8cff..54a8e5ead 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -88,21 +88,6 @@ jobs: docker run -i --rm -v $PWD:/v -w /v --net=host \ -e BAZEL_OPTIMIZATION="${BAZEL_OPTIMIZATION}" \ ubuntu:20.04 bash -x -e source.sh - - name: CentOS 7 - run: | - if [[ "${EVENT_NAME}" == "push" && "${REPO_NAME}" == "tensorflow/io" ]]; then - printf '%s\n' "${GCP_CREDS}" >service_account_creds.json - export BAZEL_OPTIMIZATION="--remote_cache=https://storage.googleapis.com/tensorflow-sigs-io --remote_upload_local_results=true --google_credentials=service_account_creds.json" - else - export BAZEL_OPTIMIZATION="--remote_cache=https://storage.googleapis.com/tensorflow-sigs-io --remote_upload_local_results=false" - fi - set -x -e - bash -x -e .github/workflows/build.space.sh - python3 .github/workflows/build.instruction.py docs/development.md "##### CentOS 7" > source.sh - cat source.sh - docker run -i --rm -v $PWD:/v -w /v --net=host \ - -e BAZEL_OPTIMIZATION="${BAZEL_OPTIMIZATION}" \ - centos:7 bash -x -e source.sh macos-bazel: name: Bazel macOS From c7e99a5711791a6cc85100adf8728ee96c4debd7 Mon Sep 17 00:00:00 2001 From: Irene Onyeneho Date: Thu, 18 Mar 2021 09:11:49 -0700 Subject: [PATCH 78/85] Adds AVRO_PARSER_NUM_MINIBATCH to override num_minibatches and logs the parsing time (#1283) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Exposes num_parallel_reads and num_parallel_calls -Exposes `num_parallel_reads` and `num_parallel_calls` in AvroRecordDataset and `make_avro_record_dataset` -Adds parameter constraints -Fixes lint issues -Adds test method for _require() function -This update adds a test to check if ValueErrors are raised when given an invalid input for num_parallel_calls * Bump Apache Arrow to 2.0.0 (#1231) * Bump Apache Arrow to 2.0.0 Also bumps Apache Thrift to 0.13.0 Signed-off-by: Yong Tang * Update code to match Arrow Signed-off-by: Yong Tang * Bump pyarrow to 2.0.0 Signed-off-by: Yong Tang * Stay with version=1 for write_feather to pass tests Signed-off-by: Yong Tang * Bump flatbuffers to 1.12.0 Signed-off-by: Yong Tang * Fix Windows issue Signed-off-by: Yong Tang * Fix tests Signed-off-by: Yong Tang * Fix Windows Signed-off-by: Yong Tang * Remove -std=c++11 and leave default -std=c++14 for arrow build Signed-off-by: Yong Tang * Update sha256 of libapr1 As the hash changed by the repo. Signed-off-by: Yong Tang * Add emulator for gcs (#1234) * Bump com_github_googleapis_google_cloud_cpp to `1.21.0` * Add gcs testbench * Bump `libcurl` to `7.69.1` * Remove the CI build for CentOS 8 (#1237) Building shared libraries on CentOS 8 is pretty much the same as on Ubuntu 20.04 except `apt` should be changed to `yum`. For that our CentOS 8 CI test is not adding a lot of value. Furthermore with the upcoming CentOS 8 change: https://www.phoronix.com/scan.php?page=news_item&px=CentOS-8-Ending-For-Stream CentOS 8 is effectively EOLed at 2021. For that we may want to drop the CentOS 8 build (only leave a comment in README.md) Note we keep CentOS 7 build for now as there are still many users using CentOS 7 and CentOS 7 will only be EOLed at 2024. We might drop CentOS 7 build in the future as well if there is similiar changes to CentOS 7 like CentOS 8. Signed-off-by: Yong Tang * add tf-c-header rule (#1244) * Skip tf-nightly:tensorflow-io==0.17.0 on API compatibility test (#1247) Signed-off-by: Yong Tang * [s3] add support for testing on macOS (#1253) * [s3] add support for testing on macOS * modify docker-compose cmd * add notebook formatting instruction in README (#1256) * [docs] Restructure README.md content (#1257) * Refactor README.md content * bump to run ci jobs * Update libtiff/libgeotiff dependency (#1258) This PR updates libtiff/libgeotiff to the latest version. Signed-off-by: Yong Tang * remove unstable elasticsearch test setup on macOS (#1263) * Exposes num_parallel_reads and num_parallel_calls (#1232) -Exposes `num_parallel_reads` and `num_parallel_calls` in AvroRecordDataset and `make_avro_record_dataset` -Adds parameter constraints -Fixes lint issues - Adds test method for _require() function -This update adds a test to check if ValueErrors are raised when given an invalid input for num_parallel_calls Co-authored-by: Abin Shahab * Added AVRO_PARSER_NUM_MINIBATCH to override num_minibatches Added AVRO_PARSER_NUM_MINIBATCH to override num_minibatches. This is recommended to be set equal to the vcore request. * Exposes num_parallel_reads and num_parallel_calls (#1232) * Exposes num_parallel_reads and num_parallel_calls -Exposes `num_parallel_reads` and `num_parallel_calls` in AvroRecordDataset and `make_avro_record_dataset` -Adds parameter constraints -Fixes lint issues * Exposes num_parallel_reads and num_parallel_calls -Exposes `num_parallel_reads` and `num_parallel_calls` in AvroRecordDataset and `make_avro_record_dataset` -Adds parameter constraints -Fixes lint issues * Exposes num_parallel_reads and num_parallel_calls -Exposes `num_parallel_reads` and `num_parallel_calls` in AvroRecordDataset and `make_avro_record_dataset` -Adds parameter constraints -Fixes lint issues * Fixes Lint Issues * Removes Optional typing for method parameter - * Adds test method for _require() function -This update adds a test to check if ValueErrors are raised when given an invalid input for num_parallel_calls * Uncomments skip for macOS pytests * Fixes Lint issues Co-authored-by: Abin Shahab * add avro tutorial testing data (#1267) Co-authored-by: Cheng Ren <1428327+chengren311@users.noreply.github.com> * Update Kafka tutorial to work with Apache Kafka (#1266) * Update Kafka tutorial to work with Apache Kafka Minor update to the Kafka tutorial to remove the dependency on Confluent's distribution of Kafka, and instead work with vanilla Apache Kafka. Signed-off-by: Dale Lane * Address review comments Remove redundant pip install commands Signed-off-by: Dale Lane * add github workflow for performance benchmarking (#1269) * add github workflow for performance benchmarking * add github-action-benchmark step * handle missing dependencies while benchmarking (#1271) * handle missing dependencies while benchmarking * setup test_sql * job name change * set auto-push to true * remove auto-push * add personal access token * use alternate method to push to gh-pages * add name to the action * use different id * modify creds * use github_token * change repo name * set auto-push * set origin and push results * set env * use PERSONAL_GITHUB_TOKEN * use push changes action * use github.head_ref to push the changes * try using fetch-depth * modify branch name * use alternative push approach * git switch - * test by merging with forked master * Disable s3 macOS for now as docker is not working on GitHub Actions for macOS (#1277) * Revert "[s3] add support for testing on macOS (#1253)" This reverts commit 81789bde99e62523ca4d9f460bb345c666902acd. Signed-off-by: Yong Tang * Update Signed-off-by: Yong Tang * rename testing data files (#1278) * Add tutorial for avro dataset API (#1250) * remove docker based mongodb tests in macos (#1279) * trigger benchmarks workflow only on commits (#1282) * Bump Apache Arrow to 3.0.0 (#1285) Signed-off-by: Yong Tang * Add bazel cache (#1287) Signed-off-by: Yong Tang * Add initial bigtable stub test (#1286) * Add initial bigtable stub test Signed-off-by: Yong Tang * Fix kokoro test Signed-off-by: Yong Tang * Add reference to github-pages benchmarks in README (#1289) * add reference to github-pages benchmarks * minor grammar change * Update README.md Co-authored-by: Yuan Tang Co-authored-by: Yuan Tang * Clear outputs (#1292) * fix kafka online-learning section in tutorial notebook (#1274) * kafka notebook fix for colab env * change timeout from 30 to 20 seconds * reduce stream_timeout * Only enable bazel caching writes for tensorflow/io github actions (#1293) This PR updates so that only GitHub actions run on tensorflow/io repo will be enabled with bazel cache writes. Without the updates, a focked repo actions will cause error. Note once bazel cache read-permissions are enabled from gcs forked repo will be able to access bazel cache (read-only). Signed-off-by: Yong Tang * Enable ready-only bazel cache (#1294) This PR enables read-only bazel cache Signed-off-by: Yong Tang * Rename tests (#1297) * Combine Ubuntu 20.04 and CentOS 7 tests into one GitHub jobs (#1299) When GitHub Actions runs it looks like there is an implicit concurrent jobs limit. As such the CentOS 7 test normally is scheduled later after other jobs completes. However, many times CentOS 7 test hangs (e.g., https://github.com/tensorflow/io/runs/1825943449). This is likely due to the CentOS 7 test is on the GitHub Actions queue for too long. This PR moves CentOS 7 to run after Ubuntu 20.04 test complete, to try to avoid hangs. Signed-off-by: Yong Tang * Update names of api tests (#1300) We renamed the tests to remove "_eager" parts. This PR updates the api test for correct filenames Signed-off-by: Yong Tang * Fix wrong benchmark tests names (#1301) Fixes wrong benchmark tests names caused by last commit Signed-off-by: Yong Tang * Patch arrow to temporarily resolve the ARROW-11518 issue (#1304) This PR patchs arrow to temporarily resolve the ARROW-11518 issue. See 1281 for details Credit to diggerk. We will update arrow after the upstream PR is merged. Signed-off-by: Yong Tang * Remove AWS headers from tensorflow, and use headers from third_party … (#1241) * Remove external headers from tensorflow, and use third_party headers instead This PR removes external headers from tensorflow, and use third_party headers instead. Signed-off-by: Yong Tang * Address review comment Signed-off-by: Yong Tang * Switch to use github to download libgeotiff (#1307) Signed-off-by: Yong Tang * Add @com_google_absl//absl/strings:cord (#1308) Fix read/STDIN_FILENO Signed-off-by: Yong Tang * Switch to modular file system for hdfs (#1309) * Switch to modular file system for hdfs This PR is part of the effort to switch to modular file system for hdfs. When TF_ENABLE_LEGACY_FILESYSTEM=1 is provided, old behavior will be preserved. Signed-off-by: Yong Tang * Build against tf-nightly Signed-off-by: Yong Tang * Update tests Signed-off-by: Yong Tang * Adjust the if else logic, follow review comment Signed-off-by: Yong Tang * Disable test_write_kafka test for now. (#1310) With tensorflow upgrade to tf-nightly, the test_write_kafka test is failing and that is block the plan to modular file system migration. This PR disables the test temporarily so that CI can continue to push tensorflow-io-nightly image (needed for modular file system migration) Signed-off-by: Yong Tang * Switch to modular file system for s3 (#1312) This PR is part of the effort to switch to modular file system for s3. When TF_ENABLE_LEGACY_FILESYSTEM=1 is provided, old behavior will be preserved. Signed-off-by: Yong Tang * Add python 3.9 on Windows (#1316) * Updates the PR to use attribute instead of Env Variable -Originally AVRO_PARSER_NUM_MINIBATCH was set as an environmental variable. Because tensorflow-io rarely uses env vars to fine tune kernal ops this was changed to an attribute. See comment here: https://github.com/tensorflow/io/pull/1283#issuecomment-767747791 * Added AVRO_PARSER_NUM_MINIBATCH to override num_minibatches Added AVRO_PARSER_NUM_MINIBATCH to override num_minibatches. This is recommended to be set equal to the vcore request. * Updates the PR to use attribute instead of Env Variable -Originally AVRO_PARSER_NUM_MINIBATCH was set as an environmental variable. Because tensorflow-io rarely uses env vars to fine tune kernal ops this was changed to an attribute. See comment here: https://github.com/tensorflow/io/pull/1283#issuecomment-767747791 * Adds addtional comments in source code for understandability Co-authored-by: Abin Shahab Co-authored-by: Yong Tang Co-authored-by: Vo Van Nghia Co-authored-by: Vignesh Kothapalli Co-authored-by: Cheng Ren Co-authored-by: Cheng Ren <1428327+chengren311@users.noreply.github.com> Co-authored-by: Dale Lane Co-authored-by: Yuan Tang Co-authored-by: Mark Daoust --- .github/workflows/build.yml | 15 +++++++ .../core/kernels/avro/parse_avro_kernels.cc | 45 +++++++++++++++---- .../kernels/avro/utils/avro_parser_tree.cc | 20 ++++++++- tensorflow_io/core/ops/avro_ops.cc | 4 ++ .../python/experimental/parse_avro_ops.py | 2 + 5 files changed, 76 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 54a8e5ead..f3e8c8cff 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -88,6 +88,21 @@ jobs: docker run -i --rm -v $PWD:/v -w /v --net=host \ -e BAZEL_OPTIMIZATION="${BAZEL_OPTIMIZATION}" \ ubuntu:20.04 bash -x -e source.sh + - name: CentOS 7 + run: | + if [[ "${EVENT_NAME}" == "push" && "${REPO_NAME}" == "tensorflow/io" ]]; then + printf '%s\n' "${GCP_CREDS}" >service_account_creds.json + export BAZEL_OPTIMIZATION="--remote_cache=https://storage.googleapis.com/tensorflow-sigs-io --remote_upload_local_results=true --google_credentials=service_account_creds.json" + else + export BAZEL_OPTIMIZATION="--remote_cache=https://storage.googleapis.com/tensorflow-sigs-io --remote_upload_local_results=false" + fi + set -x -e + bash -x -e .github/workflows/build.space.sh + python3 .github/workflows/build.instruction.py docs/development.md "##### CentOS 7" > source.sh + cat source.sh + docker run -i --rm -v $PWD:/v -w /v --net=host \ + -e BAZEL_OPTIMIZATION="${BAZEL_OPTIMIZATION}" \ + centos:7 bash -x -e source.sh macos-bazel: name: Bazel macOS diff --git a/tensorflow_io/core/kernels/avro/parse_avro_kernels.cc b/tensorflow_io/core/kernels/avro/parse_avro_kernels.cc index 6eecf841c..156d6c490 100644 --- a/tensorflow_io/core/kernels/avro/parse_avro_kernels.cc +++ b/tensorflow_io/core/kernels/avro/parse_avro_kernels.cc @@ -172,7 +172,9 @@ Status ParseAvro(const AvroParserConfig& config, const gtl::ArraySlice& serialized, thread::ThreadPool* thread_pool, AvroResult* result) { DCHECK(result != nullptr); - + using clock = std::chrono::system_clock; + using ms = std::chrono::duration; + const auto before = clock::now(); // Allocate dense output for fixed length dense values // (variable-length dense and sparse and ragged have to be buffered). /* std::vector fixed_len_dense_values(config.dense.size()); @@ -189,6 +191,10 @@ Status ParseAvro(const AvroParserConfig& config, // This parameter affects performance in a big and data-dependent way. const size_t kMiniBatchSizeBytes = 50000; + // avro_num_minibatches_ is int64 in the op interface. If not set + // the default value is 0. + size_t avro_num_minibatches_; + // Calculate number of minibatches. // In main regime make each minibatch around kMiniBatchSizeBytes bytes. // Apply 'special logic' below for small and big regimes. @@ -204,8 +210,13 @@ Status ParseAvro(const AvroParserConfig& config, minibatch_bytes = 0; } } - // 'special logic' - const size_t min_minibatches = std::min(8, serialized.size()); + if (avro_num_minibatches_) { + VLOG(5) << "Overriding num_minibatches with " << avro_num_minibatches_; + result = avro_num_minibatches_; + } + // This is to ensure users can control the num minibatches all the way down + // to size of 1(no parallelism). + const size_t min_minibatches = std::min(1, serialized.size()); const size_t max_minibatches = 64; return std::max(min_minibatches, std::min(max_minibatches, result)); @@ -245,13 +256,16 @@ Status ParseAvro(const AvroParserConfig& config, auto read_value = [&](avro::GenericDatum& d) { return range_reader.read(d); }; - + VLOG(5) << "Processing minibatch " << minibatch; status_of_minibatch[minibatch] = parser_tree.ParseValues( &buffers[minibatch], read_value, reader_schema, defaults); }; - + const auto before_parse = clock::now(); ParallelFor(ProcessMiniBatch, num_minibatches, thread_pool); - + const auto after_parse = clock::now(); + const ms parse_read_duration = after_parse - before_parse; + VLOG(5) << "PARSER_TIMING: Time spend reading and parsing " + << parse_read_duration.count() << " ms "; for (Status& status : status_of_minibatch) { TF_RETURN_IF_ERROR(status); } @@ -367,15 +381,22 @@ Status ParseAvro(const AvroParserConfig& config, return Status::OK(); }; - + const auto before_sparse_merge = clock::now(); for (size_t d = 0; d < config.sparse.size(); ++d) { TF_RETURN_IF_ERROR(MergeSparseMinibatches(d)); } - + const auto after_sparse_merge = clock::now(); + const ms s_merge_duration = after_sparse_merge - before_sparse_merge; for (size_t d = 0; d < config.dense.size(); ++d) { TF_RETURN_IF_ERROR(MergeDenseMinibatches(d)); } + const auto after_dense_merge = clock::now(); + const ms d_merge_duration = after_dense_merge - after_sparse_merge; + VLOG(5) << "PARSER_TIMING: Sparse merge duration" << s_merge_duration.count() + << " ms "; + VLOG(5) << "PARSER_TIMING: Dense merge duration" << d_merge_duration.count() + << " ms "; return Status::OK(); } @@ -388,6 +409,8 @@ class ParseAvroOp : public OpKernel { OP_REQUIRES_OK(ctx, ctx->GetAttr("sparse_types", &sparse_types_)); OP_REQUIRES_OK(ctx, ctx->GetAttr("dense_types", &dense_types_)); OP_REQUIRES_OK(ctx, ctx->GetAttr("dense_shapes", &dense_shapes_)); + OP_REQUIRES_OK( + ctx, ctx->GetAttr("avro_num_minibatches", &avro_num_minibatches_)); OP_REQUIRES_OK(ctx, ctx->GetAttr("sparse_keys", &sparse_keys_)); OP_REQUIRES_OK(ctx, ctx->GetAttr("dense_keys", &dense_keys_)); @@ -401,6 +424,11 @@ class ParseAvroOp : public OpKernel { dense_shapes_[d].dims() > 1 && dense_shapes_[d].dim_size(0) == -1; } + // Check that avro_num_minibatches is not negative + OP_REQUIRES(ctx, avro_num_minibatches_ >= 0, + errors::InvalidArgument("Need avro_num_minibatches >= 0, got ", + avro_num_minibatches_)); + string reader_schema_str; OP_REQUIRES_OK(ctx, ctx->GetAttr("reader_schema", &reader_schema_str)); @@ -495,6 +523,7 @@ class ParseAvroOp : public OpKernel { avro::ValidSchema reader_schema_; size_t num_dense_; size_t num_sparse_; + int64 avro_num_minibatches_; private: std::vector> CreateKeysAndTypes() { diff --git a/tensorflow_io/core/kernels/avro/utils/avro_parser_tree.cc b/tensorflow_io/core/kernels/avro/utils/avro_parser_tree.cc index b1b12573e..b8d5616d5 100644 --- a/tensorflow_io/core/kernels/avro/utils/avro_parser_tree.cc +++ b/tensorflow_io/core/kernels/avro/utils/avro_parser_tree.cc @@ -81,6 +81,9 @@ Status AvroParserTree::ParseValues( const std::function read_value, const avro::ValidSchema& reader_schema, const std::map& defaults) const { + using clock = std::chrono::system_clock; + using ms = std::chrono::duration; + // new assignment of all buffers TF_RETURN_IF_ERROR(InitializeValueBuffers(key_to_value)); @@ -90,11 +93,24 @@ Status AvroParserTree::ParseValues( avro::GenericDatum datum(reader_schema); bool has_value = false; - - while ((has_value = read_value(datum))) { + ms parse_duration; + ms read_duration; + while (true) { + const auto before_read = clock::now(); + if (!(has_value = read_value(datum))) { + break; + } + const auto after_read = clock::now(); TF_RETURN_IF_ERROR((*root_).Parse(key_to_value, datum, defaults)); + const auto after_parse = clock::now(); + parse_duration += after_parse - after_read; + read_duration += after_read - before_read; } + VLOG(5) << "PARSER_TIMING: Avro Read times " << read_duration.count() + << " ms "; + VLOG(5) << "PARSER_TIMING: Avro Parse times " << parse_duration.count() + << " ms "; // add end marks to all buffers for batch TF_RETURN_IF_ERROR(AddFinishMarks(key_to_value)); diff --git a/tensorflow_io/core/ops/avro_ops.cc b/tensorflow_io/core/ops/avro_ops.cc index 2432292a0..34a8b19fb 100644 --- a/tensorflow_io/core/ops/avro_ops.cc +++ b/tensorflow_io/core/ops/avro_ops.cc @@ -83,6 +83,7 @@ REGISTER_OP("IO>ParseAvro") .Output("sparse_values: sparse_types") .Output("sparse_shapes: num_sparse * int64") .Output("dense_values: dense_types") + .Attr("avro_num_minibatches: int >= 0") .Attr("num_sparse: int >= 0") .Attr("reader_schema: string") .Attr("sparse_keys: list(string) >= 0") @@ -94,6 +95,7 @@ REGISTER_OP("IO>ParseAvro") .SetShapeFn([](shape_inference::InferenceContext* c) { size_t num_dense; size_t num_sparse; + int64 avro_num_minibatches; int64 num_sparse_from_user; std::vector sparse_types; std::vector dense_types; @@ -106,6 +108,8 @@ REGISTER_OP("IO>ParseAvro") TF_RETURN_IF_ERROR(c->GetAttr("sparse_types", &sparse_types)); TF_RETURN_IF_ERROR(c->GetAttr("dense_types", &dense_types)); TF_RETURN_IF_ERROR(c->GetAttr("dense_shapes", &dense_shapes)); + TF_RETURN_IF_ERROR( + c->GetAttr("avro_num_minibatches", &avro_num_minibatches)); TF_RETURN_IF_ERROR(c->GetAttr("sparse_keys", &sparse_keys)); TF_RETURN_IF_ERROR(c->GetAttr("sparse_ranks", &sparse_ranks)); diff --git a/tensorflow_io/core/python/experimental/parse_avro_ops.py b/tensorflow_io/core/python/experimental/parse_avro_ops.py index edfbbee79..341548435 100644 --- a/tensorflow_io/core/python/experimental/parse_avro_ops.py +++ b/tensorflow_io/core/python/experimental/parse_avro_ops.py @@ -130,6 +130,7 @@ def _parse_avro( dense_defaults=None, dense_shapes=None, name=None, + avro_num_minibatches=0, ): """Parses Avro records. @@ -196,6 +197,7 @@ def _parse_avro( dense_keys=dense_keys, dense_shapes=dense_shapes, name=name, + avro_num_minibatches=avro_num_minibatches, ) (sparse_indices, sparse_values, sparse_shapes, dense_values) = outputs From de54c3c989f1b8c97c940c9ac2621538dcb12b3d Mon Sep 17 00:00:00 2001 From: markemus Date: Thu, 18 Mar 2021 15:54:18 -0400 Subject: [PATCH 79/85] Super Serial- automatically save and load TFRecords from Tensorflow datasets (#1280) * super_serial automatically creates TFRecords files from dictionary-style Tensorflow datasets. * pep8 fixes * more pep8 (undoing tensorflow 2 space tabs) * bazel changes * small change so github checks will run again * moved super_serial test to tests/ * bazel changes * moved super_serial to experimental * refactored super_serial test to work for serial_ops * bazel fixes * refactored test to load from tfio instead of full import path * licenses * bazel fixes * fixed license dates for new files * small change so tests rerun * small change so tests rerun * cleanup and bazel fix * added test to ensure proper crash occurs when trying to save in graph mode * bazel fixes * fixed imports for test * fixed imports for test * fixed yaml imports for serial_ops * fixed error path for new tf version * prevented flaky behavior in graph mode for serial_ops.py by preemptively raising an exception if graph mode is detected. * sanity check for graph execution in graph_save_fail() * it should be impossible for serial_ops not to raise an exception now outside of eager mode. Impossible. * moved eager execution check in serial_ops --- .github/workflows/build.wheel.sh | 2 +- .../python/api/experimental/serialization.py | 2 + .../core/python/experimental/serial_ops.py | 201 ++++++++++++++++++ tests/test_serial_ops.py | 89 ++++++++ 4 files changed, 293 insertions(+), 1 deletion(-) create mode 100644 tensorflow_io/core/python/experimental/serial_ops.py create mode 100644 tests/test_serial_ops.py diff --git a/.github/workflows/build.wheel.sh b/.github/workflows/build.wheel.sh index cdcae0a9c..c2a525de8 100755 --- a/.github/workflows/build.wheel.sh +++ b/.github/workflows/build.wheel.sh @@ -6,7 +6,7 @@ run_test() { entry=$1 CPYTHON_VERSION=$($entry -c 'import sys; print(str(sys.version_info[0])+str(sys.version_info[1]))') (cd wheelhouse && $entry -m pip install tensorflow_io-*-cp${CPYTHON_VERSION}-*.whl) - $entry -m pip install -q pytest pytest-benchmark boto3 fastavro avro-python3 scikit-image pandas pyarrow==3.0.0 google-cloud-pubsub==2.1.0 google-cloud-bigtable==1.6.0 google-cloud-bigquery-storage==1.1.0 google-cloud-bigquery==2.3.1 google-cloud-storage==1.32.0 + $entry -m pip install -q pytest pytest-benchmark boto3 fastavro avro-python3 scikit-image pandas pyarrow==3.0.0 google-cloud-pubsub==2.1.0 google-cloud-bigtable==1.6.0 google-cloud-bigquery-storage==1.1.0 google-cloud-bigquery==2.3.1 google-cloud-storage==1.32.0 PyYAML==5.3.1 (cd tests && $entry -m pytest --benchmark-disable -v --import-mode=append $(find . -type f \( -iname "test_*_v1.py" \))) (cd tests && $entry -m pytest --benchmark-disable -v --import-mode=append $(find . -type f \( -iname "test_*.py" ! \( -iname "test_*_v1.py" -o -iname "test_bigquery.py" \) \))) # GRPC and test_bigquery tests have to be executed separately because of https://github.com/grpc/grpc/issues/20034 diff --git a/tensorflow_io/core/python/api/experimental/serialization.py b/tensorflow_io/core/python/api/experimental/serialization.py index a6fdc246d..4dca3c941 100644 --- a/tensorflow_io/core/python/api/experimental/serialization.py +++ b/tensorflow_io/core/python/api/experimental/serialization.py @@ -19,3 +19,5 @@ decode_avro, encode_avro, ) + +from tensorflow_io.core.python.experimental.serial_ops import save_dataset, load_dataset diff --git a/tensorflow_io/core/python/experimental/serial_ops.py b/tensorflow_io/core/python/experimental/serial_ops.py new file mode 100644 index 000000000..461ad63b0 --- /dev/null +++ b/tensorflow_io/core/python/experimental/serial_ops.py @@ -0,0 +1,201 @@ +# Copyright 2021 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +"""Easily save tf.data.Datasets as tfrecord files, and restore tfrecords as Datasets. + +The goal of this module is to create a SIMPLE api to tfrecords that can be used without +learning all of the underlying mechanics. + +Users only need to deal with 2 functions: +save_dataset(dataset) +dataset = load_dataset(tfrecord, header) + +It really is that easy! + +To make this work, we create a .header file for each tfrecord which encodes metadata +needed to reconstruct the original dataset. + +Note that PyYAML (yaml) package must be installed to make use of this module. + +Saving must be done in eager mode, but loading is compatible with both eager and +graph execution modes. + +GOTCHAS: +- This module is only compatible with "dictionary-style" datasets {key: val, key2:val2,..., keyN: valN}. +- The restored dataset will have the TFRecord dtypes {float32, int64, string} instead of the original + tensor dtypes. This is always the case with TFRecord datasets, whether you use this module or not. + The original dtypes are stored in the headers if you want to restore them after loading.""" +import functools +import os +import tempfile + +import numpy as np +import tensorflow as tf + + +# The three encoding functions. +def _bytes_feature(value): + """value: list""" + return tf.train.Feature(bytes_list=tf.train.BytesList(value=value)) + + +def _float_feature(value): + """value: list""" + return tf.train.Feature(float_list=tf.train.FloatList(value=value)) + + +def _int64_feature(value): + """value: list""" + return tf.train.Feature(int64_list=tf.train.Int64List(value=value)) + + +# TODO use base_type() to ensure consistent conversion. +def np_value_to_feature(value): + """Maps dataset values to tf Features. + Only numpy types are supported since Datasets only contain tensors. + Each datatype should only have one way of being serialized.""" + if isinstance(value, np.ndarray): + # feature = _bytes_feature(value.tostring()) + if np.issubdtype(value.dtype, np.integer): + feature = _int64_feature(value.flatten()) + elif np.issubdtype(value.dtype, np.float): + feature = _float_feature(value.flatten()) + elif np.issubdtype(value.dtype, np.bool): + feature = _int64_feature(value.flatten()) + else: + raise TypeError(f"value dtype: {value.dtype} is not recognized.") + elif isinstance(value, bytes): + feature = _bytes_feature([value]) + elif np.issubdtype(type(value), np.integer): + feature = _int64_feature([value]) + elif np.issubdtype(type(value), np.float): + feature = _float_feature([value]) + + else: + raise TypeError( + f"value type: {type(value)} is not recognized. value must be a valid Numpy object." + ) + + return feature + + +def base_type(dtype): + """Returns the TFRecords allowed type corresponding to dtype.""" + int_types = [ + tf.int8, + tf.int16, + tf.int32, + tf.int64, + tf.uint8, + tf.uint16, + tf.uint32, + tf.uint64, + tf.qint8, + tf.qint16, + tf.qint32, + tf.bool, + ] + float_types = [tf.float16, tf.float32, tf.float64] + byte_types = [tf.string, bytes] + + if dtype in int_types: + new_dtype = tf.int64 + elif dtype in float_types: + new_dtype = tf.float32 + elif dtype in byte_types: + new_dtype = tf.string + else: + raise ValueError(f"dtype {dtype} is not a recognized/supported type!") + + return new_dtype + + +def build_header(dataset): + """Build header dictionary of metadata for the tensors in the dataset. This will be used when loading + the tfrecords file to reconstruct the original tensors from the raw data. Shape is stored as an array + and dtype is stored as an enumerated value (defined by tensorflow).""" + header = {} + for key in dataset.element_spec.keys(): + header[key] = { + "shape": list(dataset.element_spec[key].shape), + "dtype": dataset.element_spec[key].dtype.as_datatype_enum, + } + + return header + + +def build_feature_desc(header): + """Build feature_desc dictionary for the tensors in the dataset. This will be used to reconstruct Examples + from the tfrecords file. + + Assumes FixedLenFeatures. + If you got VarLenFeatures I feel bad for you son, + I got 115 problems but a VarLenFeature ain't one.""" + feature_desc = {} + for key, params in header.items(): + feature_desc[key] = tf.io.FixedLenFeature( + shape=params["shape"], dtype=base_type(int(params["dtype"])) + ) + + return feature_desc + + +def dataset_to_examples(ds): + """Converts a dataset to a dataset of tf.train.Example strings. Each Example is a single observation. + WARNING: Only compatible with "dictionary-style" datasets {key: val, key2:val2,..., keyN, valN}. + WARNING: Must run in eager mode!""" + # TODO handle tuples and flat datasets as well. + for x in ds: + # Each individual tensor is converted to a known serializable type. + features = {key: np_value_to_feature(value.numpy()) for key, value in x.items()} + # All features are then packaged into a single Example object. + example = tf.train.Example(features=tf.train.Features(feature=features)) + + yield example.SerializeToString() + + +def save_dataset(dataset, tfrecord_path, header_path): + """Saves a flat dataset as a tfrecord file, and builds a header file for reloading as dataset. + Must run in eager mode because it depends on dataset iteration and element_spec.""" + import yaml + + if not tf.executing_eagerly(): + raise ValueError("save_dataset() must run in eager mode!") + + # Header + header = build_header(dataset) + header_file = open(header_path, "w") + yaml.dump(header, stream=header_file) + + # Dataset + ds_examples = tf.data.Dataset.from_generator( + lambda: dataset_to_examples(dataset), output_types=tf.string + ) + writer = tf.data.experimental.TFRecordWriter(tfrecord_path) + writer.write(ds_examples) + + +# TODO-DECIDE is this yaml loader safe? +def load_dataset(tfrecord_path, header_path): + """Uses header file to predict the shape and dtypes of tensors for tf.data.""" + import yaml + + header_file = open(header_path) + header = yaml.load(header_file, Loader=yaml.FullLoader) + + feature_desc = build_feature_desc(header) + parse_func = functools.partial(tf.io.parse_single_example, features=feature_desc) + dataset = tf.data.TFRecordDataset(tfrecord_path).map(parse_func) + + return dataset diff --git a/tests/test_serial_ops.py b/tests/test_serial_ops.py new file mode 100644 index 000000000..ae461963b --- /dev/null +++ b/tests/test_serial_ops.py @@ -0,0 +1,89 @@ +# Copyright 2021 The TensorFlow Authors. All Rights Reserved. +# +# 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 the super_serial.py serialization module.""" +import os +import tempfile + +import numpy as np +import pytest +import tensorflow as tf + +import tensorflow_io as tfio + + +def test_serialization(): + """Test super serial saving and loading. + NOTE- test will only work in eager mode due to list() dataset cast.""" + savefolder = tempfile.TemporaryDirectory() + savepath = os.path.join(savefolder.name, "temp_dataset") + tfrecord_path = savepath + ".tfrecord" + header_path = savepath + ".header" + + # Data + x = np.linspace(1, 3000, num=3000).reshape(10, 10, 10, 3) + y = np.linspace(1, 10, num=10).astype(int) + ds = tf.data.Dataset.from_tensor_slices({"image": x, "label": y}) + + # Run + tfio.experimental.serialization.save_dataset( + ds, tfrecord_path=tfrecord_path, header_path=header_path + ) + new_ds = tfio.experimental.serialization.load_dataset( + tfrecord_path=tfrecord_path, header_path=header_path + ) + + # Test that values were saved and restored + assert ( + list(ds)[0]["image"].numpy()[0, 0, 0] + == list(new_ds)[0]["image"].numpy()[0, 0, 0] + ) + assert list(ds)[0]["label"] == list(new_ds)[0]["label"] + + assert ( + list(ds)[-1]["image"].numpy()[0, 0, 0] + == list(new_ds)[-1]["image"].numpy()[0, 0, 0] + ) + assert list(ds)[-1]["label"] == list(new_ds)[-1]["label"] + + # Clean up- folder will disappear on crash as well. + savefolder.cleanup() + + +@tf.function +def graph_save_fail(): + """Serial ops is expected to raise an exception when + trying to save in graph mode.""" + savefolder = tempfile.TemporaryDirectory() + savepath = os.path.join(savefolder.name, "temp_dataset") + tfrecord_path = savepath + ".tfrecord" + header_path = savepath + ".header" + + # Data + x = np.linspace(1, 3000, num=3000).reshape(10, 10, 10, 3) + y = np.linspace(1, 10, num=10).astype(int) + ds = tf.data.Dataset.from_tensor_slices({"image": x, "label": y}) + + # Run + assert os.path.isdir(savefolder.name) + assert not tf.executing_eagerly() + tfio.experimental.serialization.save_dataset( + ds, tfrecord_path=tfrecord_path, header_path=header_path + ) + + +def test_ensure_graph_fail(): + """Test that super_serial fails in graph mode.""" + with pytest.raises(ValueError): + graph_save_fail() From 9644be3b34387250aec973f246047680a7eb764c Mon Sep 17 00:00:00 2001 From: Keqiu Hu Date: Sun, 21 Mar 2021 16:08:32 -0700 Subject: [PATCH 80/85] Fix link in avro reader notebook (#1333) Correct the link to Avro Reader tests in notebook --- docs/tutorials/avro.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/avro.ipynb b/docs/tutorials/avro.ipynb index 324585af8..3d7271911 100644 --- a/docs/tutorials/avro.ipynb +++ b/docs/tutorials/avro.ipynb @@ -544,7 +544,7 @@ "id": "IF_kYz_o2DH4" }, "source": [ - "A comprehensive set of examples of Avro dataset API is provided within the tests.\n" + "A comprehensive set of examples of Avro dataset API is provided within the tests.\n" ] } ], From 8d7d28fb8410fac410cbeb9f262bb37cb61680d8 Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Mon, 22 Mar 2021 20:02:22 -0700 Subject: [PATCH 81/85] Bump abseil-cpp to 6f9d96a1f41439ac172ee2ef7ccd8edf0e5d068c (#1336) * Bump abseil-cpp to 6f9d96a1f41439ac172ee2ef7ccd8edf0e5d068c This PR bumps abseil-cpp to 6f9d96a1f41439ac172ee2ef7ccd8edf0e5d068c to fix the build issue. See related changes in tensorflow/tensorflow/commit/1c9eeb9eaa1b712d71fc29bcc9054c25c7236fa2 Signed-off-by: Yong Tang * Remove flaky CentOS 7 build Signed-off-by: Yong Tang --- .github/workflows/build.yml | 15 --------------- WORKSPACE | 8 ++++---- 2 files changed, 4 insertions(+), 19 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f3e8c8cff..54a8e5ead 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -88,21 +88,6 @@ jobs: docker run -i --rm -v $PWD:/v -w /v --net=host \ -e BAZEL_OPTIMIZATION="${BAZEL_OPTIMIZATION}" \ ubuntu:20.04 bash -x -e source.sh - - name: CentOS 7 - run: | - if [[ "${EVENT_NAME}" == "push" && "${REPO_NAME}" == "tensorflow/io" ]]; then - printf '%s\n' "${GCP_CREDS}" >service_account_creds.json - export BAZEL_OPTIMIZATION="--remote_cache=https://storage.googleapis.com/tensorflow-sigs-io --remote_upload_local_results=true --google_credentials=service_account_creds.json" - else - export BAZEL_OPTIMIZATION="--remote_cache=https://storage.googleapis.com/tensorflow-sigs-io --remote_upload_local_results=false" - fi - set -x -e - bash -x -e .github/workflows/build.space.sh - python3 .github/workflows/build.instruction.py docs/development.md "##### CentOS 7" > source.sh - cat source.sh - docker run -i --rm -v $PWD:/v -w /v --net=host \ - -e BAZEL_OPTIMIZATION="${BAZEL_OPTIMIZATION}" \ - centos:7 bash -x -e source.sh macos-bazel: name: Bazel macOS diff --git a/WORKSPACE b/WORKSPACE index d5f7ab095..45de019a8 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -400,11 +400,11 @@ http_archive( http_archive( name = "com_google_absl", - sha256 = "f368a8476f4e2e0eccf8a7318b98dafbe30b2600f4e3cf52636e5eb145aba06a", - strip_prefix = "abseil-cpp-df3ea785d8c30a9503321a3d35ee7d35808f190d", + sha256 = "62c27e7a633e965a2f40ff16b487c3b778eae440bab64cad83b34ef1cbe3aa93", + strip_prefix = "abseil-cpp-6f9d96a1f41439ac172ee2ef7ccd8edf0e5d068c", urls = [ - "https://storage.googleapis.com/mirror.tensorflow.org/github.com/abseil/abseil-cpp/archive/df3ea785d8c30a9503321a3d35ee7d35808f190d.tar.gz", - "https://github.com/abseil/abseil-cpp/archive/df3ea785d8c30a9503321a3d35ee7d35808f190d.tar.gz", + "https://storage.googleapis.com/mirror.tensorflow.org/github.com/abseil/abseil-cpp/archive/6f9d96a1f41439ac172ee2ef7ccd8edf0e5d068c.tar.gz", + "https://github.com/abseil/abseil-cpp/archive/6f9d96a1f41439ac172ee2ef7ccd8edf0e5d068c.tar.gz", ], ) From 3de431d58de81513b4d21dec8451e01370dc5ee3 Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Tue, 23 Mar 2021 11:18:41 -0700 Subject: [PATCH 82/85] Release nightly even if test fails (#1339) Signed-off-by: Yong Tang --- .github/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 54a8e5ead..87acc8d51 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -535,7 +535,7 @@ jobs: macos-nightly: name: Nightly ${{ matrix.python }} macOS if: github.event_name == 'push' - needs: [build-number, release] + needs: [build-number, macos-wheel] runs-on: macos-latest strategy: matrix: @@ -578,7 +578,7 @@ jobs: linux-nightly: name: Nightly ${{ matrix.python }} Linux if: github.event_name == 'push' - needs: [build-number, release] + needs: [build-number, linux-wheel] runs-on: ubuntu-20.04 strategy: matrix: @@ -616,7 +616,7 @@ jobs: windows-nightly: name: Nightly ${{ matrix.python }} Windows if: github.event_name == 'push' - needs: [build-number, release] + needs: [build-number, windows-wheel] runs-on: windows-latest strategy: matrix: From ef8a5d50988b4eca0ed62a0da03a80fd1c52402f Mon Sep 17 00:00:00 2001 From: Vignesh Kothapalli Date: Tue, 23 Mar 2021 23:49:10 +0530 Subject: [PATCH 83/85] remove unused/stale azure_ops (#1338) --- .../core/python/experimental/azure_ops.py | 111 ------------------ 1 file changed, 111 deletions(-) delete mode 100644 tensorflow_io/core/python/experimental/azure_ops.py diff --git a/tensorflow_io/core/python/experimental/azure_ops.py b/tensorflow_io/core/python/experimental/azure_ops.py deleted file mode 100644 index 29239d104..000000000 --- a/tensorflow_io/core/python/experimental/azure_ops.py +++ /dev/null @@ -1,111 +0,0 @@ -# Copyright 2018 The TensorFlow Authors. All Rights Reserved. -# -# 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. -# ============================================================================== - -"""tensorflow-io azure file system import""" - - -import tensorflow_io.core.python.ops # pylint: disable=unused-import - - -def authenticate_with_device_code(account_name): - """Setup storage tokens by authenticating with device code - and use management APIs. - - Args: - account_name (str): The storage account name for which to authenticate - """ - - import urllib # pylint: disable=import-outside-toplevel - import json # pylint: disable=import-outside-toplevel - import os # pylint: disable=import-outside-toplevel - from tensorflow.python.platform import ( # pylint: disable=import-outside-toplevel - tf_logging as log, - ) - - try: - from adal import ( # pylint: disable=import-outside-toplevel - AuthenticationContext, - ) - except ModuleNotFoundError: - log.error( - "Please install adal library with `python -m pip install -U adal`" - "to use the device code authentication method" - ) - return - - ctx = AuthenticationContext("https://login.microsoftonline.com/common") - - storage_resource = "https://management.azure.com/" - # Current multi-tenant client registerd in my AzureAD tenant - client_id = "8c375311-7f4c-406c-84f8-03dfe11ba2d3" - - device_code = ctx.acquire_user_code(resource=storage_resource, client_id=client_id) - - # Display authentication message to user to action in their browser - log.warn(device_code["message"]) - - token_response = ctx.acquire_token_with_device_code( - resource=storage_resource, user_code_info=device_code, client_id=client_id - ) - - headers = {"Authorization": "Bearer " + token_response["accessToken"]} - - subscription_list_req = urllib.request.Request( - url="https://management.azure.com/subscriptions?api-version=2016-06-01", - headers=headers, - ) - - with urllib.request.urlopen(subscription_list_req) as f: - subscriptions = json.load(f) - subscriptions = subscriptions["value"] - - storage_account = None - for subscription in subscriptions: - url = "https://management.azure.com/subscriptions/{}/providers/Microsoft.Storage/storageAccounts?api-version=2019-04-01".format( - subscription["subscriptionId"] - ) - storage_account_list_req = urllib.request.Request(url=url, headers=headers) - - with urllib.request.urlopen(storage_account_list_req) as f: - storage_accounts = json.load(f) - - storage_accounts = storage_accounts["value"] - account_by_name = [s for s in storage_accounts if s.get("name") == account_name] - if any(account_by_name): - storage_account = account_by_name[0] - break - - if storage_account is None: - log.error( - "Couldn't find storage account {} in any " - "available subscription".format(account_name) - ) - return - - url = "https://management.azure.com/{}/listKeys?api-version=2019-04-01".format( - storage_account["id"] - ) - storage_list_keys_req = urllib.request.Request( - url=url, headers=headers, method="POST" - ) - - with urllib.request.urlopen(storage_list_keys_req) as f: - account_keys = json.load(f) - - os.environ["TF_AZURE_STORAGE_KEY"] = account_keys["keys"][0]["value"] - log.info( - "Successfully set account key environment for {} " - "storage account".format(account_name) - ) From 4154a2cd2711b244d9291352e2162cc625b45fb2 Mon Sep 17 00:00:00 2001 From: Vo Van Nghia Date: Wed, 24 Mar 2021 17:53:14 +0100 Subject: [PATCH 84/85] gcs switch to env (#1319) * switch to env * switch to gcs on tensorflow-io according to https://github.com/tensorflow/tensorflow/pull/47247 --- .../core/plugins/file_system_plugins.cc | 3 +- tensorflow_io/core/plugins/gs/BUILD | 2 - .../core/plugins/gs/expiring_lru_cache.h | 4 +- tensorflow_io/core/plugins/gs/gcs_env.cc | 164 ------------------ tensorflow_io/core/plugins/gs/gcs_env.h | 45 ----- tensorflow_io/core/plugins/gs/gcs_helper.cc | 11 ++ tensorflow_io/core/plugins/gs/gcs_helper.h | 2 + .../core/plugins/gs/ram_file_block_cache.h | 14 +- tests/test_gcs.py | 4 +- 9 files changed, 26 insertions(+), 223 deletions(-) delete mode 100644 tensorflow_io/core/plugins/gs/gcs_env.cc delete mode 100644 tensorflow_io/core/plugins/gs/gcs_env.h diff --git a/tensorflow_io/core/plugins/file_system_plugins.cc b/tensorflow_io/core/plugins/file_system_plugins.cc index 5cf1bc4d9..5000d7c83 100644 --- a/tensorflow_io/core/plugins/file_system_plugins.cc +++ b/tensorflow_io/core/plugins/file_system_plugins.cc @@ -35,11 +35,12 @@ void TF_InitPlugin(TF_FilesystemPluginInfo* info) { tensorflow::io::hdfs::ProvideFilesystemSupportFor(&info->ops[3], "hdfse"); tensorflow::io::hdfs::ProvideFilesystemSupportFor(&info->ops[4], "viewfse"); tensorflow::io::hdfs::ProvideFilesystemSupportFor(&info->ops[5], "hare"); + tensorflow::io::gs::ProvideFilesystemSupportFor(&info->ops[6], "gse"); } else { tensorflow::io::s3::ProvideFilesystemSupportFor(&info->ops[2], "s3"); tensorflow::io::hdfs::ProvideFilesystemSupportFor(&info->ops[3], "hdfs"); tensorflow::io::hdfs::ProvideFilesystemSupportFor(&info->ops[4], "viewfs"); tensorflow::io::hdfs::ProvideFilesystemSupportFor(&info->ops[5], "har"); + tensorflow::io::gs::ProvideFilesystemSupportFor(&info->ops[6], "gs"); } - tensorflow::io::gs::ProvideFilesystemSupportFor(&info->ops[6], "gse"); } diff --git a/tensorflow_io/core/plugins/gs/BUILD b/tensorflow_io/core/plugins/gs/BUILD index 7932a1c0a..ca0495936 100644 --- a/tensorflow_io/core/plugins/gs/BUILD +++ b/tensorflow_io/core/plugins/gs/BUILD @@ -12,8 +12,6 @@ cc_library( srcs = [ "cleanup.h", "expiring_lru_cache.h", - "gcs_env.cc", - "gcs_env.h", "gcs_filesystem.cc", "gcs_helper.cc", "gcs_helper.h", diff --git a/tensorflow_io/core/plugins/gs/expiring_lru_cache.h b/tensorflow_io/core/plugins/gs/expiring_lru_cache.h index 8902bec6b..110791b97 100644 --- a/tensorflow_io/core/plugins/gs/expiring_lru_cache.h +++ b/tensorflow_io/core/plugins/gs/expiring_lru_cache.h @@ -24,8 +24,8 @@ limitations under the License. #include "absl/base/thread_annotations.h" #include "absl/synchronization/mutex.h" +#include "tensorflow/c/env.h" #include "tensorflow/c/tf_status.h" -#include "tensorflow_io/core/plugins/gs/gcs_env.h" namespace tensorflow { namespace io { @@ -44,7 +44,7 @@ class ExpiringLRUCache { /// that there is no limit on the number of entries in the cache (however, if /// `max_age` is also 0, the cache will not be populated). ExpiringLRUCache(uint64_t max_age, size_t max_entries, - std::function timer_seconds = GCSNowSeconds) + std::function timer_seconds = TF_NowSeconds) : max_age_(max_age), max_entries_(max_entries), timer_seconds_(timer_seconds) {} diff --git a/tensorflow_io/core/plugins/gs/gcs_env.cc b/tensorflow_io/core/plugins/gs/gcs_env.cc deleted file mode 100644 index a8cbb1418..000000000 --- a/tensorflow_io/core/plugins/gs/gcs_env.cc +++ /dev/null @@ -1,164 +0,0 @@ -/* Copyright 2020 The TensorFlow Authors. All Rights Reserved. - -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. -==============================================================================*/ - -#include -#include -#include -#include -#include - -#include "absl/strings/str_cat.h" -#include "absl/synchronization/mutex.h" -#if defined(_MSC_VER) -#include -#else -#include -#endif -#include -#include - -#include "tensorflow/c/logging.h" -#include "tensorflow_io/core/plugins/gs/gcs_env.h" - -namespace tensorflow { -namespace io { -namespace gs { -namespace { -// Returns a unique number every time it is called. -int64_t UniqueId() { - static absl::Mutex mu; - static int64_t id = 0; - absl::MutexLock l(&mu); - return ++id; -} - -static bool IsAbsolutePath(absl::string_view path) { - return !path.empty() && path[0] == '/'; -} - -std::string JoinPath(std::initializer_list paths) { - std::string result; - - for (absl::string_view path : paths) { - if (path.empty()) continue; - - if (result.empty()) { - result = std::string(path); - continue; - } - - if (result[result.size() - 1] == '/') { - if (IsAbsolutePath(path)) { - absl::StrAppend(&result, path.substr(1)); - } else { - absl::StrAppend(&result, path); - } - } else { - if (IsAbsolutePath(path)) { - absl::StrAppend(&result, path); - } else { - absl::StrAppend(&result, "/", path); - } - } - } - - return result; -} - -} // namespace - -uint64_t GCSNowSeconds(void) { - // TODO: Either implement NowSeconds here, or have TensorFlow API exposed - std::abort(); -} - -void GCSDefaultThreadOptions(GCSThreadOptions* options) { - options->stack_size = 0; - options->guard_size = 0; - options->numa_node = -1; -} - -std::string GCSGetTempFileName(const std::string& extension) { -#if defined(_MSC_VER) - char temp_dir[_MAX_PATH]; - DWORD retval; - retval = GetTempPath(_MAX_PATH, temp_dir); - if (retval > _MAX_PATH || retval == 0) { - TF_Log(TF_FATAL, "Cannot get the directory for temporary files."); - } - - char temp_file_name[_MAX_PATH]; - retval = GetTempFileNameA(temp_dir, "", UniqueId(), temp_file_name); - if (retval > _MAX_PATH || retval == 0) { - TF_Log(TF_FATAL, "Cannot get a temporary file in: %s", temp_dir); - } - - std::string full_tmp_file_name(temp_file_name); - full_tmp_file_name.append(extension); - return full_tmp_file_name; -#else - for (const char* dir : std::vector( - {getenv("TEST_TMPDIR"), getenv("TMPDIR"), getenv("TMP"), "/tmp"})) { - if (!dir || !dir[0]) { - continue; - } - struct stat statbuf; - if (!stat(dir, &statbuf) && S_ISDIR(statbuf.st_mode)) { - // UniqueId is added here because mkstemps is not as thread safe as it - // looks. https://github.com/tensorflow/tensorflow/issues/5804 shows - // the problem. - std::string tmp_filepath; - int fd; - if (extension.length()) { - tmp_filepath = - JoinPath({dir, absl::StrCat("tmp_file_tensorflow_", UniqueId(), - "_XXXXXX.", extension)}); - fd = mkstemps(&tmp_filepath[0], extension.length() + 1); - } else { - tmp_filepath = JoinPath( - {dir, absl::StrCat("tmp_file_tensorflow_", UniqueId(), "_XXXXXX")}); - fd = mkstemp(&tmp_filepath[0]); - } - if (fd < 0) { - TF_Log(TF_FATAL, "Failed to create temp file."); - } else { - if (close(fd) < 0) { - TF_Log(TF_ERROR, "close() failed: %s", strerror(errno)); - } - return tmp_filepath; - } - } - } - TF_Log(TF_FATAL, "No temp directory found."); - std::abort(); -#endif -} - -GCSThread* GCSStartThread(const GCSThreadOptions* options, - const char* thread_name, void (*work_func)(void*), - void* param) { - // TODO: Either implement StartThread here, or have TensorFlow API exposed - std::abort(); - return nullptr; -} - -void GCSJoinThread(GCSThread* thread) { - // TODO: Either implement JoinThread here, or have TensorFlow API exposed - std::abort(); -} - -} // namespace gs -} // namespace io -} // namespace tensorflow diff --git a/tensorflow_io/core/plugins/gs/gcs_env.h b/tensorflow_io/core/plugins/gs/gcs_env.h deleted file mode 100644 index d4af2e726..000000000 --- a/tensorflow_io/core/plugins/gs/gcs_env.h +++ /dev/null @@ -1,45 +0,0 @@ -/* Copyright 2020 The TensorFlow Authors. All Rights Reserved. - -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. -==============================================================================*/ - -#ifndef TENSORFLOW_IO_CORE_PLUGINS_GS_GCS_ENV_H_ -#define TENSORFLOW_IO_CORE_PLUGINS_GS_GCS_ENV_H_ - -#include "inttypes.h" -#include "tensorflow/c/tf_status.h" - -namespace tensorflow { -namespace io { -namespace gs { - -typedef struct GCSThread GCSThread; -typedef struct GCSThreadOptions { - size_t stack_size; - size_t guard_size; - int numa_node; -} GCSThreadOptions; - -std::string GCSGetTempFileName(const std::string& extension); -uint64_t GCSNowSeconds(void); -void GCSDefaultThreadOptions(GCSThreadOptions* options); -GCSThread* GCSStartThread(const GCSThreadOptions* options, - const char* thread_name, void (*work_func)(void*), - void* param); -void GCSJoinThread(GCSThread* thread); - -} // namespace gs -} // namespace io -} // namespace tensorflow - -#endif // TENSORFLOW_IO_CORE_PLUGINS_GS_GCS_ENV_H_ diff --git a/tensorflow_io/core/plugins/gs/gcs_helper.cc b/tensorflow_io/core/plugins/gs/gcs_helper.cc index 1368bd98a..9948e6524 100644 --- a/tensorflow_io/core/plugins/gs/gcs_helper.cc +++ b/tensorflow_io/core/plugins/gs/gcs_helper.cc @@ -16,10 +16,13 @@ limitations under the License. #include +#include #include #include #include +#include "tensorflow/c/env.h" + TempFile::TempFile(const std::string& temp_file_name, std::ios::openmode mode) : std::fstream(temp_file_name, mode), name_(temp_file_name) {} @@ -38,3 +41,11 @@ bool TempFile::truncate() { std::fstream::open(name_, std::ios::binary | std::ios::out); return std::fstream::is_open(); } + +std::string GCSGetTempFileName(const std::string& extension) { + char* raw_temp_file_name = TF_GetTempFileName(extension.c_str()); + if (!raw_temp_file_name) return ""; + std::string temp_file_name(raw_temp_file_name); + std::free(raw_temp_file_name); + return temp_file_name; +} diff --git a/tensorflow_io/core/plugins/gs/gcs_helper.h b/tensorflow_io/core/plugins/gs/gcs_helper.h index 034777c3b..33c7926a7 100644 --- a/tensorflow_io/core/plugins/gs/gcs_helper.h +++ b/tensorflow_io/core/plugins/gs/gcs_helper.h @@ -31,4 +31,6 @@ class TempFile : public std::fstream { const std::string name_; }; +std::string GCSGetTempFileName(const std::string& extension); + #endif // TENSORFLOW_C_EXPERIMENTAL_FILESYSTEM_PLUGINS_GCS_GCS_HELPER_H_ diff --git a/tensorflow_io/core/plugins/gs/ram_file_block_cache.h b/tensorflow_io/core/plugins/gs/ram_file_block_cache.h index d93be1a01..788f1baa8 100644 --- a/tensorflow_io/core/plugins/gs/ram_file_block_cache.h +++ b/tensorflow_io/core/plugins/gs/ram_file_block_cache.h @@ -27,9 +27,9 @@ limitations under the License. #include "absl/base/thread_annotations.h" #include "absl/synchronization/mutex.h" #include "absl/synchronization/notification.h" +#include "tensorflow/c/env.h" #include "tensorflow/c/logging.h" #include "tensorflow/c/tf_status.h" -#include "tensorflow_io/core/plugins/gs/gcs_env.h" namespace tensorflow { namespace io { @@ -56,19 +56,19 @@ class RamFileBlockCache { RamFileBlockCache(size_t block_size, size_t max_bytes, uint64_t max_staleness, BlockFetcher block_fetcher, - std::function timer_seconds = GCSNowSeconds) + std::function timer_seconds = TF_NowSeconds) : block_size_(block_size), max_bytes_(max_bytes), max_staleness_(max_staleness), block_fetcher_(block_fetcher), timer_seconds_(timer_seconds), pruning_thread_(nullptr, - [](GCSThread* thread) { GCSJoinThread(thread); }) { + [](TF_Thread* thread) { TF_JoinThread(thread); }) { if (max_staleness_ > 0) { - GCSThreadOptions thread_options; - GCSDefaultThreadOptions(&thread_options); + TF_ThreadOptions thread_options; + TF_DefaultThreadOptions(&thread_options); pruning_thread_.reset( - GCSStartThread(&thread_options, "TF_prune_FBC", PruneThread, this)); + TF_StartThread(&thread_options, "TF_prune_FBC", PruneThread, this)); } TF_VLog(1, "GCS file block cache is %s.\n", (IsCacheEnabled() ? "enabled" : "disabled")); @@ -236,7 +236,7 @@ class RamFileBlockCache { void RemoveBlock(BlockMap::iterator entry) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_); /// The cache pruning thread that removes files with expired blocks. - std::unique_ptr> pruning_thread_; + std::unique_ptr> pruning_thread_; /// Notification for stopping the cache pruning thread. absl::Notification stop_pruning_thread_; diff --git a/tests/test_gcs.py b/tests/test_gcs.py index 9939a775d..cef36047c 100644 --- a/tests/test_gcs.py +++ b/tests/test_gcs.py @@ -45,7 +45,7 @@ def test_read_file(): # Setup the GCS bucket and key key_name = "TEST" - bucket_name = "gse{}e".format(int(time.time())) + bucket_name = "gs{}e".format(int(time.time())) bucket = client.create_bucket(bucket_name) blob = bucket.blob(key_name) @@ -56,5 +56,5 @@ def test_read_file(): os.environ["CLOUD_STORAGE_TESTBENCH_ENDPOINT"] = "http://localhost:9099" - content = tf.io.read_file("gse://{}/{}".format(bucket_name, key_name)) + content = tf.io.read_file("gs://{}/{}".format(bucket_name, key_name)) assert content == body From 64eb761544d54109d9e53b4e76d2f531e771e44e Mon Sep 17 00:00:00 2001 From: Vo Van Nghia Date: Mon, 29 Mar 2021 21:44:14 +0200 Subject: [PATCH 85/85] improvements for `s3` environements variables (#1343) * lazy loading for `s3` environements variables * `S3_ENDPOINT` supports http/https * remove `S3_USE_HTTPS` and `S3_VERIFY_SSL` --- .../core/plugins/s3/s3_filesystem.cc | 65 +++++++------------ tensorflow_io/core/plugins/s3/s3_filesystem.h | 7 +- tests/test_s3.py | 4 +- 3 files changed, 30 insertions(+), 46 deletions(-) diff --git a/tensorflow_io/core/plugins/s3/s3_filesystem.cc b/tensorflow_io/core/plugins/s3/s3_filesystem.cc index b1b1736f5..17f4ce084 100644 --- a/tensorflow_io/core/plugins/s3/s3_filesystem.cc +++ b/tensorflow_io/core/plugins/s3/s3_filesystem.cc @@ -135,8 +135,6 @@ static Aws::Client::ClientConfiguration& GetDefaultClientConfig() { absl::MutexLock l(&cfg_lock); if (!init) { - const char* endpoint = getenv("S3_ENDPOINT"); - if (endpoint) cfg.endpointOverride = Aws::String(endpoint); const char* region = getenv("AWS_REGION"); // TODO (yongtang): `S3_REGION` should be deprecated after 2.0. if (!region) region = getenv("S3_REGION"); @@ -168,20 +166,6 @@ static Aws::Client::ClientConfiguration& GetDefaultClientConfig() { cfg.region = profiles["default"].GetRegion(); } } - const char* use_https = getenv("S3_USE_HTTPS"); - if (use_https) { - if (use_https[0] == '0') - cfg.scheme = Aws::Http::Scheme::HTTP; - else - cfg.scheme = Aws::Http::Scheme::HTTPS; - } - const char* verify_ssl = getenv("S3_VERIFY_SSL"); - if (verify_ssl) { - if (verify_ssl[0] == '0') - cfg.verifySSL = false; - else - cfg.verifySSL = true; - } // if these timeouts are low, you may see an error when // uploading/downloading large files: Unable to connect to endpoint int64_t timeout; @@ -241,6 +225,13 @@ static void GetS3Client(tf_s3_filesystem::S3File* s3_file) { tf_s3_filesystem::AWSLogSystem::ShutdownAWSLogging(); } }); + + int temp_value; + if (absl::SimpleAtoi(getenv("S3_DISABLE_MULTI_PART_DOWNLOAD"), &temp_value)) + s3_file->use_multi_part_download = (temp_value != 1); + + const char* endpoint = getenv("S3_ENDPOINT"); + if (endpoint) s3_file->s3_client->OverrideEndpoint(endpoint); } } @@ -263,15 +254,26 @@ static void GetTransferManager( absl::MutexLock l(&s3_file->initialization_lock); - if (s3_file->transfer_managers[direction].get() == nullptr) { + if (s3_file->transfer_managers.count(direction) == 0) { + uint64_t temp_value; + if (direction == Aws::Transfer::TransferDirection::UPLOAD) { + if (!absl::SimpleAtoi(getenv("S3_MULTI_PART_UPLOAD_CHUNK_SIZE"), + &temp_value)) + temp_value = kS3MultiPartUploadChunkSize; + } else if (direction == Aws::Transfer::TransferDirection::DOWNLOAD) { + if (!absl::SimpleAtoi(getenv("S3_MULTI_PART_DOWNLOAD_CHUNK_SIZE"), + &temp_value)) + temp_value = kS3MultiPartDownloadChunkSize; + } + s3_file->multi_part_chunk_sizes.emplace(direction, temp_value); + Aws::Transfer::TransferManagerConfiguration config(s3_file->executor.get()); config.s3Client = s3_file->s3_client; - config.bufferSize = s3_file->multi_part_chunk_sizes[direction]; + config.bufferSize = temp_value; // must be larger than pool size * multi part chunk size - config.transferBufferMaxHeapSize = - (kExecutorPoolSize + 1) * s3_file->multi_part_chunk_sizes[direction]; - s3_file->transfer_managers[direction] = - Aws::Transfer::TransferManager::Create(config); + config.transferBufferMaxHeapSize = (kExecutorPoolSize + 1) * temp_value; + s3_file->transfer_managers.emplace( + direction, Aws::Transfer::TransferManager::Create(config)); } } @@ -529,24 +531,7 @@ S3File::S3File() transfer_managers(), multi_part_chunk_sizes(), use_multi_part_download(false), // TODO: change to true after fix - initialization_lock() { - uint64_t temp_value; - multi_part_chunk_sizes[Aws::Transfer::TransferDirection::UPLOAD] = - absl::SimpleAtoi(getenv("S3_MULTI_PART_UPLOAD_CHUNK_SIZE"), &temp_value) - ? temp_value - : kS3MultiPartUploadChunkSize; - multi_part_chunk_sizes[Aws::Transfer::TransferDirection::DOWNLOAD] = - absl::SimpleAtoi(getenv("S3_MULTI_PART_DOWNLOAD_CHUNK_SIZE"), &temp_value) - ? temp_value - : kS3MultiPartDownloadChunkSize; - use_multi_part_download = - absl::SimpleAtoi(getenv("S3_DISABLE_MULTI_PART_DOWNLOAD"), &temp_value) - ? (temp_value != 1) - : use_multi_part_download; - transfer_managers.emplace(Aws::Transfer::TransferDirection::UPLOAD, nullptr); - transfer_managers.emplace(Aws::Transfer::TransferDirection::DOWNLOAD, - nullptr); -} + initialization_lock() {} void Init(TF_Filesystem* filesystem, TF_Status* status) { filesystem->plugin_filesystem = new S3File(); TF_SetStatus(status, TF_OK, ""); diff --git a/tensorflow_io/core/plugins/s3/s3_filesystem.h b/tensorflow_io/core/plugins/s3/s3_filesystem.h index 85e71b0d6..882785854 100644 --- a/tensorflow_io/core/plugins/s3/s3_filesystem.h +++ b/tensorflow_io/core/plugins/s3/s3_filesystem.h @@ -60,11 +60,12 @@ typedef struct S3File { std::shared_ptr s3_client; std::shared_ptr executor; // We need 2 `TransferManager`, for multipart upload/download. - Aws::Map> + Aws::UnorderedMap> transfer_managers; // Sizes to split objects during multipart upload/download. - Aws::Map multi_part_chunk_sizes; + Aws::UnorderedMap + multi_part_chunk_sizes; bool use_multi_part_download; absl::Mutex initialization_lock; S3File(); diff --git a/tests/test_s3.py b/tests/test_s3.py index 6f008f3a9..08f928380 100644 --- a/tests/test_s3.py +++ b/tests/test_s3.py @@ -51,9 +51,7 @@ def test_read_file(): response = client.get_object(Bucket=bucket_name, Key=key_name) assert response["Body"].read() == body - os.environ["S3_ENDPOINT"] = "localhost:4566" - os.environ["S3_USE_HTTPS"] = "0" - os.environ["S3_VERIFY_SSL"] = "0" + os.environ["S3_ENDPOINT"] = "http://localhost:4566" content = tf.io.read_file("s3://{}/{}".format(bucket_name, key_name)) assert content == body

t^!IOBB81wl#c{ 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