From a849563cc75434408b70045ac5781bd0cc94b935 Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:04:40 -0700 Subject: [PATCH 01/47] changes without context autosynth cannot find the source of changes triggered by earlier changes in this repository, or by version upgrades to tools such as linters. --- .coveragerc | 21 +- .github/snippet-bot.yml | 0 .kokoro/test-samples.sh | 8 +- .kokoro/trampoline_v2.sh | 2 +- README.rst | 75 +- docs/cloudtrace_v1/services.rst | 6 + docs/cloudtrace_v1/trace_service.rst | 11 + docs/cloudtrace_v1/types.rst | 7 + docs/cloudtrace_v2/services.rst | 6 + docs/cloudtrace_v2/trace_service.rst | 6 + docs/cloudtrace_v2/types.rst | 7 + docs/conf.py | 9 +- google/cloud/trace_v1/proto/trace.proto | 305 ++++ google/cloud/trace_v2/proto/trace.proto | 378 +++++ google/cloud/trace_v2/proto/tracing.proto | 79 + google/devtools/cloudtrace/__init__.py | 40 + google/devtools/cloudtrace/py.typed | 2 + .../cloudtrace_v1}/__init__.py | 0 google/devtools/cloudtrace_v1/py.typed | 2 + .../cloudtrace_v1/services/__init__.py | 16 + .../services/trace_service/__init__.py | 24 + .../services/trace_service/async_client.py | 438 +++++ .../services/trace_service/client.py | 577 +++++++ .../services/trace_service/pagers.py | 157 ++ .../trace_service/transports/__init__.py | 35 + .../services/trace_service/transports/base.py | 188 ++ .../services/trace_service/transports/grpc.py | 312 ++++ .../trace_service/transports/grpc_asyncio.py | 318 ++++ .../devtools/cloudtrace_v1/types/__init__.py | 36 + google/devtools/cloudtrace_v1/types/trace.py | 329 ++++ .../cloudtrace_v2}/__init__.py | 0 google/devtools/cloudtrace_v2/py.typed | 2 + .../cloudtrace_v2/services/__init__.py | 16 + .../services/trace_service/__init__.py | 24 + .../services/trace_service/async_client.py | 341 ++++ .../services/trace_service/client.py | 509 ++++++ .../trace_service/transports/__init__.py | 35 + .../services/trace_service/transports/base.py | 151 ++ .../services/trace_service/transports/grpc.py | 283 ++++ .../trace_service/transports/grpc_asyncio.py | 287 ++++ .../devtools/cloudtrace_v2/types/__init__.py | 34 + google/devtools/cloudtrace_v2/types/trace.py | 513 ++++++ .../devtools/cloudtrace_v2/types/tracing.py | 47 + noxfile.py | 2 +- samples/snippets/noxfile.py | 26 +- scripts/decrypt-secrets.sh | 15 +- scripts/fixup_cloudtrace_v1_keywords.py | 181 ++ scripts/fixup_cloudtrace_v2_keywords.py | 180 ++ synth.metadata | 84 +- tests/unit/gapic/cloudtrace_v1/__init__.py | 16 + .../gapic/cloudtrace_v1/test_trace_service.py | 1507 +++++++++++++++++ tests/unit/gapic/cloudtrace_v2/__init__.py | 16 + .../gapic/cloudtrace_v2/test_trace_service.py | 1316 ++++++++++++++ 53 files changed, 8833 insertions(+), 146 deletions(-) delete mode 100644 .github/snippet-bot.yml create mode 100644 docs/cloudtrace_v1/services.rst create mode 100644 docs/cloudtrace_v1/trace_service.rst create mode 100644 docs/cloudtrace_v1/types.rst create mode 100644 docs/cloudtrace_v2/services.rst create mode 100644 docs/cloudtrace_v2/trace_service.rst create mode 100644 docs/cloudtrace_v2/types.rst create mode 100644 google/cloud/trace_v1/proto/trace.proto create mode 100644 google/cloud/trace_v2/proto/trace.proto create mode 100644 google/cloud/trace_v2/proto/tracing.proto create mode 100644 google/devtools/cloudtrace/__init__.py create mode 100644 google/devtools/cloudtrace/py.typed rename google/{cloud/trace_v1 => devtools/cloudtrace_v1}/__init__.py (100%) create mode 100644 google/devtools/cloudtrace_v1/py.typed create mode 100644 google/devtools/cloudtrace_v1/services/__init__.py create mode 100644 google/devtools/cloudtrace_v1/services/trace_service/__init__.py create mode 100644 google/devtools/cloudtrace_v1/services/trace_service/async_client.py create mode 100644 google/devtools/cloudtrace_v1/services/trace_service/client.py create mode 100644 google/devtools/cloudtrace_v1/services/trace_service/pagers.py create mode 100644 google/devtools/cloudtrace_v1/services/trace_service/transports/__init__.py create mode 100644 google/devtools/cloudtrace_v1/services/trace_service/transports/base.py create mode 100644 google/devtools/cloudtrace_v1/services/trace_service/transports/grpc.py create mode 100644 google/devtools/cloudtrace_v1/services/trace_service/transports/grpc_asyncio.py create mode 100644 google/devtools/cloudtrace_v1/types/__init__.py create mode 100644 google/devtools/cloudtrace_v1/types/trace.py rename google/{cloud/trace_v2 => devtools/cloudtrace_v2}/__init__.py (100%) create mode 100644 google/devtools/cloudtrace_v2/py.typed create mode 100644 google/devtools/cloudtrace_v2/services/__init__.py create mode 100644 google/devtools/cloudtrace_v2/services/trace_service/__init__.py create mode 100644 google/devtools/cloudtrace_v2/services/trace_service/async_client.py create mode 100644 google/devtools/cloudtrace_v2/services/trace_service/client.py create mode 100644 google/devtools/cloudtrace_v2/services/trace_service/transports/__init__.py create mode 100644 google/devtools/cloudtrace_v2/services/trace_service/transports/base.py create mode 100644 google/devtools/cloudtrace_v2/services/trace_service/transports/grpc.py create mode 100644 google/devtools/cloudtrace_v2/services/trace_service/transports/grpc_asyncio.py create mode 100644 google/devtools/cloudtrace_v2/types/__init__.py create mode 100644 google/devtools/cloudtrace_v2/types/trace.py create mode 100644 google/devtools/cloudtrace_v2/types/tracing.py create mode 100644 scripts/fixup_cloudtrace_v1_keywords.py create mode 100644 scripts/fixup_cloudtrace_v2_keywords.py create mode 100644 tests/unit/gapic/cloudtrace_v1/__init__.py create mode 100644 tests/unit/gapic/cloudtrace_v1/test_trace_service.py create mode 100644 tests/unit/gapic/cloudtrace_v2/__init__.py create mode 100644 tests/unit/gapic/cloudtrace_v2/test_trace_service.py diff --git a/.coveragerc b/.coveragerc index b16f6fc2..d348d12f 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,26 +1,11 @@ -# -*- coding: utf-8 -*- -# -# Copyright 2020 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 -# -# https://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. - [run] branch = True [report] fail_under = 100 show_missing = True -omit = google/cloud/devtools/cloudtrace/__init__.py +omit = + google/devtools/cloudtrace/__init__.py exclude_lines = # Re-enable the standard pragma pragma: NO COVER @@ -30,4 +15,4 @@ exclude_lines = # This is added at the module level as a safeguard for if someone # generates the code and tries to run it without pip installing. This # makes it virtually impossible to test properly. - except pkg_resources.DistributionNotFound \ No newline at end of file + except pkg_resources.DistributionNotFound diff --git a/.github/snippet-bot.yml b/.github/snippet-bot.yml deleted file mode 100644 index e69de29b..00000000 diff --git a/.kokoro/test-samples.sh b/.kokoro/test-samples.sh index 2dbdf8a7..140899ff 100755 --- a/.kokoro/test-samples.sh +++ b/.kokoro/test-samples.sh @@ -81,11 +81,11 @@ for file in samples/**/requirements.txt; do python3.6 -m nox -s "$RUN_TESTS_SESSION" EXIT=$? - # If this is a periodic build, send the test log to the FlakyBot. - # See https://github.com/googleapis/repo-automation-bots/tree/master/packages/flakybot. + # If this is a periodic build, send the test log to the Build Cop Bot. + # See https://github.com/googleapis/repo-automation-bots/tree/master/packages/buildcop. if [[ $KOKORO_BUILD_ARTIFACTS_SUBDIR = *"periodic"* ]]; then - chmod +x $KOKORO_GFILE_DIR/linux_amd64/flakybot - $KOKORO_GFILE_DIR/linux_amd64/flakybot + chmod +x $KOKORO_GFILE_DIR/linux_amd64/buildcop + $KOKORO_GFILE_DIR/linux_amd64/buildcop fi if [[ $EXIT -ne 0 ]]; then diff --git a/.kokoro/trampoline_v2.sh b/.kokoro/trampoline_v2.sh index 4af6cdc2..719bcd5b 100755 --- a/.kokoro/trampoline_v2.sh +++ b/.kokoro/trampoline_v2.sh @@ -159,7 +159,7 @@ if [[ -n "${KOKORO_BUILD_ID:-}" ]]; then "KOKORO_GITHUB_COMMIT" "KOKORO_GITHUB_PULL_REQUEST_NUMBER" "KOKORO_GITHUB_PULL_REQUEST_COMMIT" - # For FlakyBot + # For Build Cop Bot "KOKORO_GITHUB_COMMIT_URL" "KOKORO_GITHUB_PULL_REQUEST_URL" ) diff --git a/README.rst b/README.rst index 597d5ca4..29b312a3 100644 --- a/README.rst +++ b/README.rst @@ -1,25 +1,5 @@ -Python Client for Cloud Trace API -======================================= - -|ga| |pypi| |versions| - -The `Cloud Trace API`_ sends application trace data to Cloud Trace -for viewing. Trace data is collected for all App Engine applications by -default. Trace data from other applications can be provided using this API. - -- `Client Library Documentation`_ -- `Product Documentation`_ - -.. |ga| image:: https://img.shields.io/badge/support-ga-gold.svg - :target: https://github.com/googleapis/google-cloud-python/blob/master/README.rst#general-availability -.. |pypi| image:: https://img.shields.io/pypi/v/google-cloud-trace.svg - :target: https://pypi.org/project/google-cloud-trace/ -.. |versions| image:: https://img.shields.io/pypi/pyversions/google-cloud-trace.svg - :target: https://pypi.org/project/google-cloud-trace/ -.. _Cloud Trace API: https://cloud.google.com/trace -.. _Client Library Documentation: https://googleapis.dev/python/cloudtrace/latest -.. _Product Documentation: https://cloud.google.com/trace - +Python Client for Google Devtools Cloudtrace API +================================================= Quick Start ----------- @@ -27,14 +7,14 @@ Quick Start In order to use this library, you first need to go through the following steps: 1. `Select or create a Cloud Platform project.`_ -2. `Enable the trace API.`_ -3. `Setup Authentication.`_ +2. `Enable billing for your project.`_ +3. Enable the Google Devtools Cloudtrace API. +4. `Setup Authentication.`_ .. _Select or create a Cloud Platform project.: https://console.cloud.google.com/project -.. _Enable the trace API.: https://cloud.google.com/trace +.. _Enable billing for your project.: https://cloud.google.com/billing/docs/how-to/modify-project#enable_billing_for_a_project .. _Setup Authentication.: https://googleapis.dev/python/google-api-core/latest/auth.html - Installation ~~~~~~~~~~~~ @@ -42,23 +22,11 @@ Install this library in a `virtualenv`_ using pip. `virtualenv`_ is a tool to create isolated Python environments. The basic problem it addresses is one of dependencies and versions, and indirectly permissions. -With `virtualenv`_, it's possible to install this library without needing -system install permissions, and without clashing with the installed system +With `virtualenv`_, it's possible to install this library without needing system +install permissions, and without clashing with the installed system dependencies. -.. _virtualenv: https://virtualenv.pypa.io/en/latest/ - - -Supported Python Versions -^^^^^^^^^^^^^^^^^^^^^^^^^ -Python >= 3.6 - - -Unsupported Python Versions -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Python == 2.7. - -The last version of this library compatible with Python 2.7 is google-cloud-trace==0.24.0 +.. _`virtualenv`: https://virtualenv.pypa.io/en/latest/ Mac/Linux @@ -66,10 +34,9 @@ Mac/Linux .. code-block:: console - pip install virtualenv - virtualenv + python3 -m venv source /bin/activate - /bin/pip install google-cloud-trace + /bin/pip install /path/to/library Windows @@ -77,22 +44,6 @@ Windows .. code-block:: console - pip install virtualenv - virtualenv + python3 -m venv \Scripts\activate - \Scripts\pip.exe install google-cloud-trace - -For more information on setting up your Python development environment, -such as installing ``pip`` and ``virtualenv`` on your system, please refer -to `Python Development Environment Setup Guide`_ for Google Cloud Platform. - -.. _Python Development Environment Setup Guide: https://cloud.google.com/python/setup - - -Next Steps -~~~~~~~~~~ - -- Read the `Client Library Documentation`_ for Cloud Trace API - to see other available methods on the client. -- Read the `Product documentation`_ to learn more about the product and see - How-to Guides. + \Scripts\pip.exe install \path\to\library diff --git a/docs/cloudtrace_v1/services.rst b/docs/cloudtrace_v1/services.rst new file mode 100644 index 00000000..b143d452 --- /dev/null +++ b/docs/cloudtrace_v1/services.rst @@ -0,0 +1,6 @@ +Services for Google Devtools Cloudtrace v1 API +============================================== +.. toctree:: + :maxdepth: 2 + + trace_service diff --git a/docs/cloudtrace_v1/trace_service.rst b/docs/cloudtrace_v1/trace_service.rst new file mode 100644 index 00000000..522d3f8c --- /dev/null +++ b/docs/cloudtrace_v1/trace_service.rst @@ -0,0 +1,11 @@ +TraceService +------------------------------ + +.. automodule:: google.devtools.cloudtrace_v1.services.trace_service + :members: + :inherited-members: + + +.. automodule:: google.devtools.cloudtrace_v1.services.trace_service.pagers + :members: + :inherited-members: diff --git a/docs/cloudtrace_v1/types.rst b/docs/cloudtrace_v1/types.rst new file mode 100644 index 00000000..1ece94cd --- /dev/null +++ b/docs/cloudtrace_v1/types.rst @@ -0,0 +1,7 @@ +Types for Google Devtools Cloudtrace v1 API +=========================================== + +.. automodule:: google.devtools.cloudtrace_v1.types + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/cloudtrace_v2/services.rst b/docs/cloudtrace_v2/services.rst new file mode 100644 index 00000000..9233e023 --- /dev/null +++ b/docs/cloudtrace_v2/services.rst @@ -0,0 +1,6 @@ +Services for Google Devtools Cloudtrace v2 API +============================================== +.. toctree:: + :maxdepth: 2 + + trace_service diff --git a/docs/cloudtrace_v2/trace_service.rst b/docs/cloudtrace_v2/trace_service.rst new file mode 100644 index 00000000..c8e912b4 --- /dev/null +++ b/docs/cloudtrace_v2/trace_service.rst @@ -0,0 +1,6 @@ +TraceService +------------------------------ + +.. automodule:: google.devtools.cloudtrace_v2.services.trace_service + :members: + :inherited-members: diff --git a/docs/cloudtrace_v2/types.rst b/docs/cloudtrace_v2/types.rst new file mode 100644 index 00000000..afdd0fe9 --- /dev/null +++ b/docs/cloudtrace_v2/types.rst @@ -0,0 +1,7 @@ +Types for Google Devtools Cloudtrace v2 API +=========================================== + +.. automodule:: google.devtools.cloudtrace_v2.types + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/conf.py b/docs/conf.py index 8f206ded..6345740f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -29,7 +29,7 @@ # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. -needs_sphinx = "1.5.5" +needs_sphinx = "1.6.3" # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom @@ -39,7 +39,6 @@ "sphinx.ext.autosummary", "sphinx.ext.intersphinx", "sphinx.ext.coverage", - "sphinx.ext.doctest", "sphinx.ext.napoleon", "sphinx.ext.todo", "sphinx.ext.viewcode", @@ -345,10 +344,10 @@ # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = { - "python": ("https://python.readthedocs.org/en/latest/", None), - "google-auth": ("https://googleapis.dev/python/google-auth/latest/", None), + "python": ("http://python.readthedocs.org/en/latest/", None), + "google-auth": ("https://google-auth.readthedocs.io/en/stable", None), "google.api_core": ("https://googleapis.dev/python/google-api-core/latest/", None,), - "grpc": ("https://grpc.github.io/grpc/python/", None), + "grpc": ("https://grpc.io/grpc/python/", None), } diff --git a/google/cloud/trace_v1/proto/trace.proto b/google/cloud/trace_v1/proto/trace.proto new file mode 100644 index 00000000..d3948fa1 --- /dev/null +++ b/google/cloud/trace_v1/proto/trace.proto @@ -0,0 +1,305 @@ +// Copyright 2020 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. + +syntax = "proto3"; + +package google.devtools.cloudtrace.v1; + +import "google/api/client.proto"; +import "google/api/field_behavior.proto"; +import "google/api/resource.proto"; +import "google/protobuf/empty.proto"; +import "google/protobuf/timestamp.proto"; +import "google/api/annotations.proto"; + +option csharp_namespace = "Google.Cloud.Trace.V1"; +option go_package = "google.golang.org/genproto/googleapis/devtools/cloudtrace/v1;cloudtrace"; +option java_multiple_files = true; +option java_outer_classname = "TraceProto"; +option java_package = "com.google.devtools.cloudtrace.v1"; +option php_namespace = "Google\\Cloud\\Trace\\V1"; +option ruby_package = "Google::Cloud::Trace::V1"; + +// This file describes an API for collecting and viewing traces and spans +// within a trace. A Trace is a collection of spans corresponding to a single +// operation or set of operations for an application. A span is an individual +// timed event which forms a node of the trace tree. Spans for a single trace +// may span multiple services. +service TraceService { + option (google.api.default_host) = "cloudtrace.googleapis.com"; + option (google.api.oauth_scopes) = + "https://www.googleapis.com/auth/cloud-platform," + "https://www.googleapis.com/auth/trace.append," + "https://www.googleapis.com/auth/trace.readonly"; + + // Returns of a list of traces that match the specified filter conditions. + rpc ListTraces(ListTracesRequest) returns (ListTracesResponse) { + option (google.api.http) = { + get: "/v1/projects/{project_id}/traces" + }; + option (google.api.method_signature) = "project_id"; + } + + // Gets a single trace by its ID. + rpc GetTrace(GetTraceRequest) returns (Trace) { + option (google.api.http) = { + get: "/v1/projects/{project_id}/traces/{trace_id}" + }; + option (google.api.method_signature) = "project_id,trace_id"; + } + + // Sends new traces to Stackdriver Trace or updates existing traces. If the ID + // of a trace that you send matches that of an existing trace, any fields + // in the existing trace and its spans are overwritten by the provided values, + // and any new fields provided are merged with the existing trace data. If the + // ID does not match, a new trace is created. + rpc PatchTraces(PatchTracesRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + patch: "/v1/projects/{project_id}/traces" + body: "traces" + }; + option (google.api.method_signature) = "project_id,traces"; + } +} + +// A trace describes how long it takes for an application to perform an +// operation. It consists of a set of spans, each of which represent a single +// timed event within the operation. +message Trace { + // Project ID of the Cloud project where the trace data is stored. + string project_id = 1; + + // Globally unique identifier for the trace. This identifier is a 128-bit + // numeric value formatted as a 32-byte hex string. For example, + // `382d4f4c6b7bb2f4a972559d9085001d`. + string trace_id = 2; + + // Collection of spans in the trace. + repeated TraceSpan spans = 3; +} + +// List of new or updated traces. +message Traces { + // List of traces. + repeated Trace traces = 1; +} + +// A span represents a single timed event within a trace. Spans can be nested +// and form a trace tree. Often, a trace contains a root span that describes the +// end-to-end latency of an operation and, optionally, one or more subspans for +// its suboperations. Spans do not need to be contiguous. There may be gaps +// between spans in a trace. +message TraceSpan { + // Type of span. Can be used to specify additional relationships between spans + // in addition to a parent/child relationship. + enum SpanKind { + // Unspecified. + SPAN_KIND_UNSPECIFIED = 0; + + // Indicates that the span covers server-side handling of an RPC or other + // remote network request. + RPC_SERVER = 1; + + // Indicates that the span covers the client-side wrapper around an RPC or + // other remote request. + RPC_CLIENT = 2; + } + + // Identifier for the span. Must be a 64-bit integer other than 0 and + // unique within a trace. For example, `2205310701640571284`. + fixed64 span_id = 1; + + // Distinguishes between spans generated in a particular context. For example, + // two spans with the same name may be distinguished using `RPC_CLIENT` + // and `RPC_SERVER` to identify queueing latency associated with the span. + SpanKind kind = 2; + + // Name of the span. Must be less than 128 bytes. The span name is sanitized + // and displayed in the Stackdriver Trace tool in the + // Google Cloud Platform Console. + // The name may be a method name or some other per-call site name. + // For the same executable and the same call point, a best practice is + // to use a consistent name, which makes it easier to correlate + // cross-trace spans. + string name = 3; + + // Start time of the span in nanoseconds from the UNIX epoch. + google.protobuf.Timestamp start_time = 4; + + // End time of the span in nanoseconds from the UNIX epoch. + google.protobuf.Timestamp end_time = 5; + + // Optional. ID of the parent span, if any. + fixed64 parent_span_id = 6 [(google.api.field_behavior) = OPTIONAL]; + + // Collection of labels associated with the span. Label keys must be less than + // 128 bytes. Label values must be less than 16 kilobytes (10MB for + // `/stacktrace` values). + // + // Some predefined label keys exist, or you may create your own. When creating + // your own, we recommend the following formats: + // + // * `/category/product/key` for agents of well-known products (e.g. + // `/db/mongodb/read_size`). + // * `short_host/path/key` for domain-specific keys (e.g. + // `foo.com/myproduct/bar`) + // + // Predefined labels include: + // + // * `/agent` + // * `/component` + // * `/error/message` + // * `/error/name` + // * `/http/client_city` + // * `/http/client_country` + // * `/http/client_protocol` + // * `/http/client_region` + // * `/http/host` + // * `/http/method` + // * `/http/path` + // * `/http/redirected_url` + // * `/http/request/size` + // * `/http/response/size` + // * `/http/route` + // * `/http/status_code` + // * `/http/url` + // * `/http/user_agent` + // * `/pid` + // * `/stacktrace` + // * `/tid` + map labels = 7; +} + +// The request message for the `ListTraces` method. All fields are required +// unless specified. +message ListTracesRequest { + // Type of data returned for traces in the list. + enum ViewType { + // Default is `MINIMAL` if unspecified. + VIEW_TYPE_UNSPECIFIED = 0; + + // Minimal view of the trace record that contains only the project + // and trace IDs. + MINIMAL = 1; + + // Root span view of the trace record that returns the root spans along + // with the minimal trace data. + ROOTSPAN = 2; + + // Complete view of the trace record that contains the actual trace data. + // This is equivalent to calling the REST `get` or RPC `GetTrace` method + // using the ID of each listed trace. + COMPLETE = 3; + } + + // Required. ID of the Cloud project where the trace data is stored. + string project_id = 1 [(google.api.field_behavior) = REQUIRED]; + + // Optional. Type of data returned for traces in the list. Default is + // `MINIMAL`. + ViewType view = 2 [(google.api.field_behavior) = OPTIONAL]; + + // Optional. Maximum number of traces to return. If not specified or <= 0, the + // implementation selects a reasonable value. The implementation may + // return fewer traces than the requested page size. + int32 page_size = 3 [(google.api.field_behavior) = OPTIONAL]; + + // Token identifying the page of results to return. If provided, use the + // value of the `next_page_token` field from a previous request. + string page_token = 4; + + // Start of the time interval (inclusive) during which the trace data was + // collected from the application. + google.protobuf.Timestamp start_time = 5; + + // End of the time interval (inclusive) during which the trace data was + // collected from the application. + google.protobuf.Timestamp end_time = 6; + + // Optional. A filter against labels for the request. + // + // By default, searches use prefix matching. To specify exact match, prepend + // a plus symbol (`+`) to the search term. + // Multiple terms are ANDed. Syntax: + // + // * `root:NAME_PREFIX` or `NAME_PREFIX`: Return traces where any root + // span starts with `NAME_PREFIX`. + // * `+root:NAME` or `+NAME`: Return traces where any root span's name is + // exactly `NAME`. + // * `span:NAME_PREFIX`: Return traces where any span starts with + // `NAME_PREFIX`. + // * `+span:NAME`: Return traces where any span's name is exactly + // `NAME`. + // * `latency:DURATION`: Return traces whose overall latency is + // greater or equal to than `DURATION`. Accepted units are nanoseconds + // (`ns`), milliseconds (`ms`), and seconds (`s`). Default is `ms`. For + // example, `latency:24ms` returns traces whose overall latency + // is greater than or equal to 24 milliseconds. + // * `label:LABEL_KEY`: Return all traces containing the specified + // label key (exact match, case-sensitive) regardless of the key:value + // pair's value (including empty values). + // * `LABEL_KEY:VALUE_PREFIX`: Return all traces containing the specified + // label key (exact match, case-sensitive) whose value starts with + // `VALUE_PREFIX`. Both a key and a value must be specified. + // * `+LABEL_KEY:VALUE`: Return all traces containing a key:value pair + // exactly matching the specified text. Both a key and a value must be + // specified. + // * `method:VALUE`: Equivalent to `/http/method:VALUE`. + // * `url:VALUE`: Equivalent to `/http/url:VALUE`. + string filter = 7 [(google.api.field_behavior) = OPTIONAL]; + + // Optional. Field used to sort the returned traces. + // Can be one of the following: + // + // * `trace_id` + // * `name` (`name` field of root span in the trace) + // * `duration` (difference between `end_time` and `start_time` fields of + // the root span) + // * `start` (`start_time` field of the root span) + // + // Descending order can be specified by appending `desc` to the sort field + // (for example, `name desc`). + // + // Only one sort field is permitted. + string order_by = 8 [(google.api.field_behavior) = OPTIONAL]; +} + +// The response message for the `ListTraces` method. +message ListTracesResponse { + // List of trace records as specified by the view parameter. + repeated Trace traces = 1; + + // If defined, indicates that there are more traces that match the request + // and that this value should be passed to the next request to continue + // retrieving additional traces. + string next_page_token = 2; +} + +// The request message for the `GetTrace` method. +message GetTraceRequest { + // Required. ID of the Cloud project where the trace data is stored. + string project_id = 1 [(google.api.field_behavior) = REQUIRED]; + + // Required. ID of the trace to return. + string trace_id = 2 [(google.api.field_behavior) = REQUIRED]; +} + +// The request message for the `PatchTraces` method. +message PatchTracesRequest { + // Required. ID of the Cloud project where the trace data is stored. + string project_id = 1 [(google.api.field_behavior) = REQUIRED]; + + // Required. The body of the message. + Traces traces = 2 [(google.api.field_behavior) = REQUIRED]; +} diff --git a/google/cloud/trace_v2/proto/trace.proto b/google/cloud/trace_v2/proto/trace.proto new file mode 100644 index 00000000..66669aa6 --- /dev/null +++ b/google/cloud/trace_v2/proto/trace.proto @@ -0,0 +1,378 @@ +// Copyright 2020 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. + +syntax = "proto3"; + +package google.devtools.cloudtrace.v2; + +import "google/api/field_behavior.proto"; +import "google/api/resource.proto"; +import "google/protobuf/timestamp.proto"; +import "google/protobuf/wrappers.proto"; +import "google/rpc/status.proto"; +import "google/api/annotations.proto"; + +option csharp_namespace = "Google.Cloud.Trace.V2"; +option go_package = "google.golang.org/genproto/googleapis/devtools/cloudtrace/v2;cloudtrace"; +option java_multiple_files = true; +option java_outer_classname = "TraceProto"; +option java_package = "com.google.devtools.cloudtrace.v2"; +option php_namespace = "Google\\Cloud\\Trace\\V2"; +option ruby_package = "Google::Cloud::Trace::V2"; + +// A span represents a single operation within a trace. Spans can be +// nested to form a trace tree. Often, a trace contains a root span +// that describes the end-to-end latency, and one or more subspans for +// its sub-operations. A trace can also contain multiple root spans, +// or none at all. Spans do not need to be contiguous—there may be +// gaps or overlaps between spans in a trace. +message Span { + option (google.api.resource) = { + type: "cloudtrace.googleapis.com/Span" + pattern: "projects/{project}/traces/{trace}/spans/{span}" + }; + + // A set of attributes, each in the format `[KEY]:[VALUE]`. + message Attributes { + // The set of attributes. Each attribute's key can be up to 128 bytes + // long. The value can be a string up to 256 bytes, a signed 64-bit integer, + // or the Boolean values `true` and `false`. For example: + // + // "/instance_id": { "string_value": { "value": "my-instance" } } + // "/http/request_bytes": { "int_value": 300 } + // "abc.com/myattribute": { "bool_value": false } + map attribute_map = 1; + + // The number of attributes that were discarded. Attributes can be discarded + // because their keys are too long or because there are too many attributes. + // If this value is 0 then all attributes are valid. + int32 dropped_attributes_count = 2; + } + + // A time-stamped annotation or message event in the Span. + message TimeEvent { + // Text annotation with a set of attributes. + message Annotation { + // A user-supplied message describing the event. The maximum length for + // the description is 256 bytes. + TruncatableString description = 1; + + // A set of attributes on the annotation. You can have up to 4 attributes + // per Annotation. + Attributes attributes = 2; + } + + // An event describing a message sent/received between Spans. + message MessageEvent { + // Indicates whether the message was sent or received. + enum Type { + // Unknown event type. + TYPE_UNSPECIFIED = 0; + + // Indicates a sent message. + SENT = 1; + + // Indicates a received message. + RECEIVED = 2; + } + + // Type of MessageEvent. Indicates whether the message was sent or + // received. + Type type = 1; + + // An identifier for the MessageEvent's message that can be used to match + // SENT and RECEIVED MessageEvents. It is recommended to be unique within + // a Span. + int64 id = 2; + + // The number of uncompressed bytes sent or received. + int64 uncompressed_size_bytes = 3; + + // The number of compressed bytes sent or received. If missing assumed to + // be the same size as uncompressed. + int64 compressed_size_bytes = 4; + } + + // The timestamp indicating the time the event occurred. + google.protobuf.Timestamp time = 1; + + // A `TimeEvent` can contain either an `Annotation` object or a + // `MessageEvent` object, but not both. + oneof value { + // Text annotation with a set of attributes. + Annotation annotation = 2; + + // An event describing a message sent/received between Spans. + MessageEvent message_event = 3; + } + } + + // A collection of `TimeEvent`s. A `TimeEvent` is a time-stamped annotation + // on the span, consisting of either user-supplied key:value pairs, or + // details of a message sent/received between Spans. + message TimeEvents { + // A collection of `TimeEvent`s. + repeated TimeEvent time_event = 1; + + // The number of dropped annotations in all the included time events. + // If the value is 0, then no annotations were dropped. + int32 dropped_annotations_count = 2; + + // The number of dropped message events in all the included time events. + // If the value is 0, then no message events were dropped. + int32 dropped_message_events_count = 3; + } + + // A pointer from the current span to another span in the same trace or in a + // different trace. For example, this can be used in batching operations, + // where a single batch handler processes multiple requests from different + // traces or when the handler receives a request from a different project. + message Link { + // The relationship of the current span relative to the linked span: child, + // parent, or unspecified. + enum Type { + // The relationship of the two spans is unknown. + TYPE_UNSPECIFIED = 0; + + // The linked span is a child of the current span. + CHILD_LINKED_SPAN = 1; + + // The linked span is a parent of the current span. + PARENT_LINKED_SPAN = 2; + } + + // The [TRACE_ID] for a trace within a project. + string trace_id = 1; + + // The [SPAN_ID] for a span within a trace. + string span_id = 2; + + // The relationship of the current span relative to the linked span. + Type type = 3; + + // A set of attributes on the link. You have have up to 32 attributes per + // link. + Attributes attributes = 4; + } + + // A collection of links, which are references from this span to a span + // in the same or different trace. + message Links { + // A collection of links. + repeated Link link = 1; + + // The number of dropped links after the maximum size was enforced. If + // this value is 0, then no links were dropped. + int32 dropped_links_count = 2; + } + + // Type of span. Can be used to specify additional relationships between spans + // in addition to a parent/child relationship. + enum SpanKind { + // Unspecified. Do NOT use as default. + // Implementations MAY assume SpanKind.INTERNAL to be default. + SPAN_KIND_UNSPECIFIED = 0; + + // Indicates that the span is used internally. Default value. + INTERNAL = 1; + + // Indicates that the span covers server-side handling of an RPC or other + // remote network request. + SERVER = 2; + + // Indicates that the span covers the client-side wrapper around an RPC or + // other remote request. + CLIENT = 3; + + // Indicates that the span describes producer sending a message to a broker. + // Unlike client and server, there is no direct critical path latency + // relationship between producer and consumer spans (e.g. publishing a + // message to a pubsub service). + PRODUCER = 4; + + // Indicates that the span describes consumer receiving a message from a + // broker. Unlike client and server, there is no direct critical path + // latency relationship between producer and consumer spans (e.g. receiving + // a message from a pubsub service subscription). + CONSUMER = 5; + } + + // Required. The resource name of the span in the following format: + // + // projects/[PROJECT_ID]/traces/[TRACE_ID]/spans/[SPAN_ID] + // + // [TRACE_ID] is a unique identifier for a trace within a project; + // it is a 32-character hexadecimal encoding of a 16-byte array. + // + // [SPAN_ID] is a unique identifier for a span within a trace; it + // is a 16-character hexadecimal encoding of an 8-byte array. + string name = 1 [(google.api.field_behavior) = REQUIRED]; + + // Required. The [SPAN_ID] portion of the span's resource name. + string span_id = 2 [(google.api.field_behavior) = REQUIRED]; + + // The [SPAN_ID] of this span's parent span. If this is a root span, + // then this field must be empty. + string parent_span_id = 3; + + // Required. A description of the span's operation (up to 128 bytes). + // Stackdriver Trace displays the description in the + // Google Cloud Platform Console. + // For example, the display name can be a qualified method name or a file name + // and a line number where the operation is called. A best practice is to use + // the same display name within an application and at the same call point. + // This makes it easier to correlate spans in different traces. + TruncatableString display_name = 4 [(google.api.field_behavior) = REQUIRED]; + + // Required. The start time of the span. On the client side, this is the time kept by + // the local machine where the span execution starts. On the server side, this + // is the time when the server's application handler starts running. + google.protobuf.Timestamp start_time = 5 [(google.api.field_behavior) = REQUIRED]; + + // Required. The end time of the span. On the client side, this is the time kept by + // the local machine where the span execution ends. On the server side, this + // is the time when the server application handler stops running. + google.protobuf.Timestamp end_time = 6 [(google.api.field_behavior) = REQUIRED]; + + // A set of attributes on the span. You can have up to 32 attributes per + // span. + Attributes attributes = 7; + + // Stack trace captured at the start of the span. + StackTrace stack_trace = 8; + + // A set of time events. You can have up to 32 annotations and 128 message + // events per span. + TimeEvents time_events = 9; + + // Links associated with the span. You can have up to 128 links per Span. + Links links = 10; + + // Optional. The final status for this span. + google.rpc.Status status = 11 [(google.api.field_behavior) = OPTIONAL]; + + // Optional. Set this parameter to indicate whether this span is in + // the same process as its parent. If you do not set this parameter, + // Stackdriver Trace is unable to take advantage of this helpful + // information. + google.protobuf.BoolValue same_process_as_parent_span = 12 [(google.api.field_behavior) = OPTIONAL]; + + // Optional. The number of child spans that were generated while this span + // was active. If set, allows implementation to detect missing child spans. + google.protobuf.Int32Value child_span_count = 13 [(google.api.field_behavior) = OPTIONAL]; + + // Optional. Distinguishes between spans generated in a particular context. For example, + // two spans with the same name may be distinguished using `CLIENT` (caller) + // and `SERVER` (callee) to identify an RPC call. + SpanKind span_kind = 14 [(google.api.field_behavior) = OPTIONAL]; +} + +// The allowed types for [VALUE] in a `[KEY]:[VALUE]` attribute. +message AttributeValue { + // The type of the value. + oneof value { + // A string up to 256 bytes long. + TruncatableString string_value = 1; + + // A 64-bit signed integer. + int64 int_value = 2; + + // A Boolean value represented by `true` or `false`. + bool bool_value = 3; + } +} + +// A call stack appearing in a trace. +message StackTrace { + // Represents a single stack frame in a stack trace. + message StackFrame { + // The fully-qualified name that uniquely identifies the function or + // method that is active in this frame (up to 1024 bytes). + TruncatableString function_name = 1; + + // An un-mangled function name, if `function_name` is + // [mangled](http://www.avabodh.com/cxxin/namemangling.html). The name can + // be fully-qualified (up to 1024 bytes). + TruncatableString original_function_name = 2; + + // The name of the source file where the function call appears (up to 256 + // bytes). + TruncatableString file_name = 3; + + // The line number in `file_name` where the function call appears. + int64 line_number = 4; + + // The column number where the function call appears, if available. + // This is important in JavaScript because of its anonymous functions. + int64 column_number = 5; + + // The binary module from where the code was loaded. + Module load_module = 6; + + // The version of the deployed source code (up to 128 bytes). + TruncatableString source_version = 7; + } + + // A collection of stack frames, which can be truncated. + message StackFrames { + // Stack frames in this call stack. + repeated StackFrame frame = 1; + + // The number of stack frames that were dropped because there + // were too many stack frames. + // If this value is 0, then no stack frames were dropped. + int32 dropped_frames_count = 2; + } + + // Stack frames in this stack trace. A maximum of 128 frames are allowed. + StackFrames stack_frames = 1; + + // The hash ID is used to conserve network bandwidth for duplicate + // stack traces within a single trace. + // + // Often multiple spans will have identical stack traces. + // The first occurrence of a stack trace should contain both the + // `stackFrame` content and a value in `stackTraceHashId`. + // + // Subsequent spans within the same request can refer + // to that stack trace by only setting `stackTraceHashId`. + int64 stack_trace_hash_id = 2; +} + +// Binary module. +message Module { + // For example: main binary, kernel modules, and dynamic libraries + // such as libc.so, sharedlib.so (up to 256 bytes). + TruncatableString module = 1; + + // A unique identifier for the module, usually a hash of its + // contents (up to 128 bytes). + TruncatableString build_id = 2; +} + +// Represents a string that might be shortened to a specified length. +message TruncatableString { + // The shortened string. For example, if the original string is 500 + // bytes long and the limit of the string is 128 bytes, then + // `value` contains the first 128 bytes of the 500-byte string. + // + // Truncation always happens on a UTF8 character boundary. If there + // are multi-byte characters in the string, then the length of the + // shortened string might be less than the size limit. + string value = 1; + + // The number of bytes removed from the original string. If this + // value is 0, then the string was not shortened. + int32 truncated_byte_count = 2; +} diff --git a/google/cloud/trace_v2/proto/tracing.proto b/google/cloud/trace_v2/proto/tracing.proto new file mode 100644 index 00000000..a25a0dd7 --- /dev/null +++ b/google/cloud/trace_v2/proto/tracing.proto @@ -0,0 +1,79 @@ +// Copyright 2020 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. + +syntax = "proto3"; + +package google.devtools.cloudtrace.v2; + +import "google/api/annotations.proto"; +import "google/api/client.proto"; +import "google/api/field_behavior.proto"; +import "google/api/resource.proto"; +import "google/devtools/cloudtrace/v2/trace.proto"; +import "google/protobuf/empty.proto"; +import "google/protobuf/timestamp.proto"; + +option csharp_namespace = "Google.Cloud.Trace.V2"; +option go_package = "google.golang.org/genproto/googleapis/devtools/cloudtrace/v2;cloudtrace"; +option java_multiple_files = true; +option java_outer_classname = "TracingProto"; +option java_package = "com.google.devtools.cloudtrace.v2"; +option php_namespace = "Google\\Cloud\\Trace\\V2"; +option ruby_package = "Google::Cloud::Trace::V2"; + +// This file describes an API for collecting and viewing traces and spans +// within a trace. A Trace is a collection of spans corresponding to a single +// operation or set of operations for an application. A span is an individual +// timed event which forms a node of the trace tree. A single trace may +// contain span(s) from multiple services. +service TraceService { + option (google.api.default_host) = "cloudtrace.googleapis.com"; + option (google.api.oauth_scopes) = + "https://www.googleapis.com/auth/cloud-platform," + "https://www.googleapis.com/auth/trace.append"; + + // Sends new spans to new or existing traces. You cannot update + // existing spans. + rpc BatchWriteSpans(BatchWriteSpansRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + post: "/v2/{name=projects/*}/traces:batchWrite" + body: "*" + }; + option (google.api.method_signature) = "name,spans"; + } + + // Creates a new span. + rpc CreateSpan(Span) returns (Span) { + option (google.api.http) = { + post: "/v2/{name=projects/*/traces/*/spans/*}" + body: "*" + }; + } +} + +// The request message for the `BatchWriteSpans` method. +message BatchWriteSpansRequest { + // Required. The name of the project where the spans belong. The format is + // `projects/[PROJECT_ID]`. + string name = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "cloudresourcemanager.googleapis.com/Project" + } + ]; + + // Required. A list of new spans. The span names must not match existing + // spans, or the results are undefined. + repeated Span spans = 2 [(google.api.field_behavior) = REQUIRED]; +} diff --git a/google/devtools/cloudtrace/__init__.py b/google/devtools/cloudtrace/__init__.py new file mode 100644 index 00000000..27a6340d --- /dev/null +++ b/google/devtools/cloudtrace/__init__.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 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. +# + +from google.devtools.cloudtrace_v2.services.trace_service.async_client import ( + TraceServiceAsyncClient, +) +from google.devtools.cloudtrace_v2.services.trace_service.client import ( + TraceServiceClient, +) +from google.devtools.cloudtrace_v2.types.trace import AttributeValue +from google.devtools.cloudtrace_v2.types.trace import Module +from google.devtools.cloudtrace_v2.types.trace import Span +from google.devtools.cloudtrace_v2.types.trace import StackTrace +from google.devtools.cloudtrace_v2.types.trace import TruncatableString +from google.devtools.cloudtrace_v2.types.tracing import BatchWriteSpansRequest + +__all__ = ( + "AttributeValue", + "BatchWriteSpansRequest", + "Module", + "Span", + "StackTrace", + "TraceServiceAsyncClient", + "TraceServiceClient", + "TruncatableString", +) diff --git a/google/devtools/cloudtrace/py.typed b/google/devtools/cloudtrace/py.typed new file mode 100644 index 00000000..c9f021ea --- /dev/null +++ b/google/devtools/cloudtrace/py.typed @@ -0,0 +1,2 @@ +# Marker file for PEP 561. +# The google-devtools-cloudtrace package uses inline types. diff --git a/google/cloud/trace_v1/__init__.py b/google/devtools/cloudtrace_v1/__init__.py similarity index 100% rename from google/cloud/trace_v1/__init__.py rename to google/devtools/cloudtrace_v1/__init__.py diff --git a/google/devtools/cloudtrace_v1/py.typed b/google/devtools/cloudtrace_v1/py.typed new file mode 100644 index 00000000..c9f021ea --- /dev/null +++ b/google/devtools/cloudtrace_v1/py.typed @@ -0,0 +1,2 @@ +# Marker file for PEP 561. +# The google-devtools-cloudtrace package uses inline types. diff --git a/google/devtools/cloudtrace_v1/services/__init__.py b/google/devtools/cloudtrace_v1/services/__init__.py new file mode 100644 index 00000000..42ffdf2b --- /dev/null +++ b/google/devtools/cloudtrace_v1/services/__init__.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 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. +# diff --git a/google/devtools/cloudtrace_v1/services/trace_service/__init__.py b/google/devtools/cloudtrace_v1/services/trace_service/__init__.py new file mode 100644 index 00000000..e06e796c --- /dev/null +++ b/google/devtools/cloudtrace_v1/services/trace_service/__init__.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 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. +# + +from .client import TraceServiceClient +from .async_client import TraceServiceAsyncClient + +__all__ = ( + "TraceServiceClient", + "TraceServiceAsyncClient", +) diff --git a/google/devtools/cloudtrace_v1/services/trace_service/async_client.py b/google/devtools/cloudtrace_v1/services/trace_service/async_client.py new file mode 100644 index 00000000..29f96058 --- /dev/null +++ b/google/devtools/cloudtrace_v1/services/trace_service/async_client.py @@ -0,0 +1,438 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 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. +# + +from collections import OrderedDict +import functools +import re +from typing import Dict, Sequence, Tuple, Type, Union +import pkg_resources + +import google.api_core.client_options as ClientOptions # type: ignore +from google.api_core import exceptions # type: ignore +from google.api_core import gapic_v1 # type: ignore +from google.api_core import retry as retries # type: ignore +from google.auth import credentials # type: ignore +from google.oauth2 import service_account # type: ignore + +from google.devtools.cloudtrace_v1.services.trace_service import pagers +from google.devtools.cloudtrace_v1.types import trace + +from .transports.base import TraceServiceTransport, DEFAULT_CLIENT_INFO +from .transports.grpc_asyncio import TraceServiceGrpcAsyncIOTransport +from .client import TraceServiceClient + + +class TraceServiceAsyncClient: + """This file describes an API for collecting and viewing traces + and spans within a trace. A Trace is a collection of spans + corresponding to a single operation or set of operations for an + application. A span is an individual timed event which forms a + node of the trace tree. Spans for a single trace may span + multiple services. + """ + + _client: TraceServiceClient + + DEFAULT_ENDPOINT = TraceServiceClient.DEFAULT_ENDPOINT + DEFAULT_MTLS_ENDPOINT = TraceServiceClient.DEFAULT_MTLS_ENDPOINT + + common_billing_account_path = staticmethod( + TraceServiceClient.common_billing_account_path + ) + parse_common_billing_account_path = staticmethod( + TraceServiceClient.parse_common_billing_account_path + ) + + common_folder_path = staticmethod(TraceServiceClient.common_folder_path) + parse_common_folder_path = staticmethod(TraceServiceClient.parse_common_folder_path) + + common_organization_path = staticmethod(TraceServiceClient.common_organization_path) + parse_common_organization_path = staticmethod( + TraceServiceClient.parse_common_organization_path + ) + + common_project_path = staticmethod(TraceServiceClient.common_project_path) + parse_common_project_path = staticmethod( + TraceServiceClient.parse_common_project_path + ) + + common_location_path = staticmethod(TraceServiceClient.common_location_path) + parse_common_location_path = staticmethod( + TraceServiceClient.parse_common_location_path + ) + + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + TraceServiceAsyncClient: The constructed client. + """ + return TraceServiceClient.from_service_account_info.__func__(TraceServiceAsyncClient, info, *args, **kwargs) # type: ignore + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + TraceServiceAsyncClient: The constructed client. + """ + return TraceServiceClient.from_service_account_file.__func__(TraceServiceAsyncClient, filename, *args, **kwargs) # type: ignore + + from_service_account_json = from_service_account_file + + @property + def transport(self) -> TraceServiceTransport: + """Return the transport used by the client instance. + + Returns: + TraceServiceTransport: The transport used by the client instance. + """ + return self._client.transport + + get_transport_class = functools.partial( + type(TraceServiceClient).get_transport_class, type(TraceServiceClient) + ) + + def __init__( + self, + *, + credentials: credentials.Credentials = None, + transport: Union[str, TraceServiceTransport] = "grpc_asyncio", + client_options: ClientOptions = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the trace service client. + + Args: + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + transport (Union[str, ~.TraceServiceTransport]): The + transport to use. If set to None, a transport is chosen + automatically. + client_options (ClientOptions): Custom options for the client. It + won't take effect if a ``transport`` instance is provided. + (1) The ``api_endpoint`` property can be used to override the + default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT + environment variable can also be used to override the endpoint: + "always" (always use the default mTLS endpoint), "never" (always + use the default regular endpoint) and "auto" (auto switch to the + default mTLS endpoint if client certificate is present, this is + the default value). However, the ``api_endpoint`` property takes + precedence if provided. + (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable + is "true", then the ``client_cert_source`` property can be used + to provide client certificate for mutual TLS transport. If + not provided, the default SSL client certificate will be used if + present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not + set, no client certificate will be used. + + Raises: + google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport + creation failed for any reason. + """ + + self._client = TraceServiceClient( + credentials=credentials, + transport=transport, + client_options=client_options, + client_info=client_info, + ) + + async def list_traces( + self, + request: trace.ListTracesRequest = None, + *, + project_id: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListTracesAsyncPager: + r"""Returns of a list of traces that match the specified + filter conditions. + + Args: + request (:class:`google.devtools.cloudtrace_v1.types.ListTracesRequest`): + The request object. The request message for the + `ListTraces` method. All fields are required unless + specified. + project_id (:class:`str`): + Required. ID of the Cloud project + where the trace data is stored. + + This corresponds to the ``project_id`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.devtools.cloudtrace_v1.services.trace_service.pagers.ListTracesAsyncPager: + The response message for the ListTraces method. + + Iterating over this object will yield results and + resolve additional pages automatically. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([project_id]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = trace.ListTracesRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if project_id is not None: + request.project_id = project_id + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.list_traces, + default_retry=retries.Retry( + initial=0.1, + maximum=1.0, + multiplier=1.2, + predicate=retries.if_exception_type( + exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, + ), + deadline=45.0, + ), + default_timeout=45.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # This method is paged; wrap the response in a pager, which provides + # an `__aiter__` convenience method. + response = pagers.ListTracesAsyncPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + async def get_trace( + self, + request: trace.GetTraceRequest = None, + *, + project_id: str = None, + trace_id: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> trace.Trace: + r"""Gets a single trace by its ID. + + Args: + request (:class:`google.devtools.cloudtrace_v1.types.GetTraceRequest`): + The request object. The request message for the + `GetTrace` method. + project_id (:class:`str`): + Required. ID of the Cloud project + where the trace data is stored. + + This corresponds to the ``project_id`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + trace_id (:class:`str`): + Required. ID of the trace to return. + This corresponds to the ``trace_id`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.devtools.cloudtrace_v1.types.Trace: + A trace describes how long it takes + for an application to perform an + operation. It consists of a set of + spans, each of which represent a single + timed event within the operation. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([project_id, trace_id]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = trace.GetTraceRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if project_id is not None: + request.project_id = project_id + if trace_id is not None: + request.trace_id = trace_id + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.get_trace, + default_retry=retries.Retry( + initial=0.1, + maximum=1.0, + multiplier=1.2, + predicate=retries.if_exception_type( + exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, + ), + deadline=45.0, + ), + default_timeout=45.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def patch_traces( + self, + request: trace.PatchTracesRequest = None, + *, + project_id: str = None, + traces: trace.Traces = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> None: + r"""Sends new traces to Stackdriver Trace or updates + existing traces. If the ID of a trace that you send + matches that of an existing trace, any fields in the + existing trace and its spans are overwritten by the + provided values, and any new fields provided are merged + with the existing trace data. If the ID does not match, + a new trace is created. + + Args: + request (:class:`google.devtools.cloudtrace_v1.types.PatchTracesRequest`): + The request object. The request message for the + `PatchTraces` method. + project_id (:class:`str`): + Required. ID of the Cloud project + where the trace data is stored. + + This corresponds to the ``project_id`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + traces (:class:`google.devtools.cloudtrace_v1.types.Traces`): + Required. The body of the message. + This corresponds to the ``traces`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([project_id, traces]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = trace.PatchTracesRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if project_id is not None: + request.project_id = project_id + if traces is not None: + request.traces = traces + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.patch_traces, + default_retry=retries.Retry( + initial=0.1, + maximum=1.0, + multiplier=1.2, + predicate=retries.if_exception_type( + exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, + ), + deadline=45.0, + ), + default_timeout=45.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Send the request. + await rpc( + request, retry=retry, timeout=timeout, metadata=metadata, + ) + + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-devtools-cloudtrace", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("TraceServiceAsyncClient",) diff --git a/google/devtools/cloudtrace_v1/services/trace_service/client.py b/google/devtools/cloudtrace_v1/services/trace_service/client.py new file mode 100644 index 00000000..6c5aeee3 --- /dev/null +++ b/google/devtools/cloudtrace_v1/services/trace_service/client.py @@ -0,0 +1,577 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 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. +# + +from collections import OrderedDict +from distutils import util +import os +import re +from typing import Callable, Dict, Optional, Sequence, Tuple, Type, Union +import pkg_resources + +from google.api_core import client_options as client_options_lib # type: ignore +from google.api_core import exceptions # type: ignore +from google.api_core import gapic_v1 # type: ignore +from google.api_core import retry as retries # type: ignore +from google.auth import credentials # type: ignore +from google.auth.transport import mtls # type: ignore +from google.auth.transport.grpc import SslCredentials # type: ignore +from google.auth.exceptions import MutualTLSChannelError # type: ignore +from google.oauth2 import service_account # type: ignore + +from google.devtools.cloudtrace_v1.services.trace_service import pagers +from google.devtools.cloudtrace_v1.types import trace + +from .transports.base import TraceServiceTransport, DEFAULT_CLIENT_INFO +from .transports.grpc import TraceServiceGrpcTransport +from .transports.grpc_asyncio import TraceServiceGrpcAsyncIOTransport + + +class TraceServiceClientMeta(type): + """Metaclass for the TraceService client. + + This provides class-level methods for building and retrieving + support objects (e.g. transport) without polluting the client instance + objects. + """ + + _transport_registry = OrderedDict() # type: Dict[str, Type[TraceServiceTransport]] + _transport_registry["grpc"] = TraceServiceGrpcTransport + _transport_registry["grpc_asyncio"] = TraceServiceGrpcAsyncIOTransport + + def get_transport_class(cls, label: str = None,) -> Type[TraceServiceTransport]: + """Return an appropriate transport class. + + Args: + label: The name of the desired transport. If none is + provided, then the first transport in the registry is used. + + Returns: + The transport class to use. + """ + # If a specific transport is requested, return that one. + if label: + return cls._transport_registry[label] + + # No transport is requested; return the default (that is, the first one + # in the dictionary). + return next(iter(cls._transport_registry.values())) + + +class TraceServiceClient(metaclass=TraceServiceClientMeta): + """This file describes an API for collecting and viewing traces + and spans within a trace. A Trace is a collection of spans + corresponding to a single operation or set of operations for an + application. A span is an individual timed event which forms a + node of the trace tree. Spans for a single trace may span + multiple services. + """ + + @staticmethod + def _get_default_mtls_endpoint(api_endpoint): + """Convert api endpoint to mTLS endpoint. + Convert "*.sandbox.googleapis.com" and "*.googleapis.com" to + "*.mtls.sandbox.googleapis.com" and "*.mtls.googleapis.com" respectively. + Args: + api_endpoint (Optional[str]): the api endpoint to convert. + Returns: + str: converted mTLS api endpoint. + """ + if not api_endpoint: + return api_endpoint + + mtls_endpoint_re = re.compile( + r"(?P[^.]+)(?P\.mtls)?(?P\.sandbox)?(?P\.googleapis\.com)?" + ) + + m = mtls_endpoint_re.match(api_endpoint) + name, mtls, sandbox, googledomain = m.groups() + if mtls or not googledomain: + return api_endpoint + + if sandbox: + return api_endpoint.replace( + "sandbox.googleapis.com", "mtls.sandbox.googleapis.com" + ) + + return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com") + + DEFAULT_ENDPOINT = "cloudtrace.googleapis.com" + DEFAULT_MTLS_ENDPOINT = _get_default_mtls_endpoint.__func__( # type: ignore + DEFAULT_ENDPOINT + ) + + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + TraceServiceClient: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_info(info) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + TraceServiceClient: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_file(filename) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + + from_service_account_json = from_service_account_file + + @property + def transport(self) -> TraceServiceTransport: + """Return the transport used by the client instance. + + Returns: + TraceServiceTransport: The transport used by the client instance. + """ + return self._transport + + @staticmethod + def common_billing_account_path(billing_account: str,) -> str: + """Return a fully-qualified billing_account string.""" + return "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + + @staticmethod + def parse_common_billing_account_path(path: str) -> Dict[str, str]: + """Parse a billing_account path into its component segments.""" + m = re.match(r"^billingAccounts/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_folder_path(folder: str,) -> str: + """Return a fully-qualified folder string.""" + return "folders/{folder}".format(folder=folder,) + + @staticmethod + def parse_common_folder_path(path: str) -> Dict[str, str]: + """Parse a folder path into its component segments.""" + m = re.match(r"^folders/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_organization_path(organization: str,) -> str: + """Return a fully-qualified organization string.""" + return "organizations/{organization}".format(organization=organization,) + + @staticmethod + def parse_common_organization_path(path: str) -> Dict[str, str]: + """Parse a organization path into its component segments.""" + m = re.match(r"^organizations/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_project_path(project: str,) -> str: + """Return a fully-qualified project string.""" + return "projects/{project}".format(project=project,) + + @staticmethod + def parse_common_project_path(path: str) -> Dict[str, str]: + """Parse a project path into its component segments.""" + m = re.match(r"^projects/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_location_path(project: str, location: str,) -> str: + """Return a fully-qualified location string.""" + return "projects/{project}/locations/{location}".format( + project=project, location=location, + ) + + @staticmethod + def parse_common_location_path(path: str) -> Dict[str, str]: + """Parse a location path into its component segments.""" + m = re.match(r"^projects/(?P.+?)/locations/(?P.+?)$", path) + return m.groupdict() if m else {} + + def __init__( + self, + *, + credentials: Optional[credentials.Credentials] = None, + transport: Union[str, TraceServiceTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the trace service client. + + Args: + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + transport (Union[str, TraceServiceTransport]): The + transport to use. If set to None, a transport is chosen + automatically. + client_options (google.api_core.client_options.ClientOptions): Custom options for the + client. It won't take effect if a ``transport`` instance is provided. + (1) The ``api_endpoint`` property can be used to override the + default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT + environment variable can also be used to override the endpoint: + "always" (always use the default mTLS endpoint), "never" (always + use the default regular endpoint) and "auto" (auto switch to the + default mTLS endpoint if client certificate is present, this is + the default value). However, the ``api_endpoint`` property takes + precedence if provided. + (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable + is "true", then the ``client_cert_source`` property can be used + to provide client certificate for mutual TLS transport. If + not provided, the default SSL client certificate will be used if + present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not + set, no client certificate will be used. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + + Raises: + google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport + creation failed for any reason. + """ + if isinstance(client_options, dict): + client_options = client_options_lib.from_dict(client_options) + if client_options is None: + client_options = client_options_lib.ClientOptions() + + # Create SSL credentials for mutual TLS if needed. + use_client_cert = bool( + util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) + ) + + client_cert_source_func = None + is_mtls = False + if use_client_cert: + if client_options.client_cert_source: + is_mtls = True + client_cert_source_func = client_options.client_cert_source + else: + is_mtls = mtls.has_default_client_cert_source() + client_cert_source_func = ( + mtls.default_client_cert_source() if is_mtls else None + ) + + # Figure out which api endpoint to use. + if client_options.api_endpoint is not None: + api_endpoint = client_options.api_endpoint + else: + use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto") + if use_mtls_env == "never": + api_endpoint = self.DEFAULT_ENDPOINT + elif use_mtls_env == "always": + api_endpoint = self.DEFAULT_MTLS_ENDPOINT + elif use_mtls_env == "auto": + api_endpoint = ( + self.DEFAULT_MTLS_ENDPOINT if is_mtls else self.DEFAULT_ENDPOINT + ) + else: + raise MutualTLSChannelError( + "Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted values: never, auto, always" + ) + + # Save or instantiate the transport. + # Ordinarily, we provide the transport, but allowing a custom transport + # instance provides an extensibility point for unusual situations. + if isinstance(transport, TraceServiceTransport): + # transport is a TraceServiceTransport instance. + if credentials or client_options.credentials_file: + raise ValueError( + "When providing a transport instance, " + "provide its credentials directly." + ) + if client_options.scopes: + raise ValueError( + "When providing a transport instance, " + "provide its scopes directly." + ) + self._transport = transport + else: + Transport = type(self).get_transport_class(transport) + self._transport = Transport( + credentials=credentials, + credentials_file=client_options.credentials_file, + host=api_endpoint, + scopes=client_options.scopes, + client_cert_source_for_mtls=client_cert_source_func, + quota_project_id=client_options.quota_project_id, + client_info=client_info, + ) + + def list_traces( + self, + request: trace.ListTracesRequest = None, + *, + project_id: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListTracesPager: + r"""Returns of a list of traces that match the specified + filter conditions. + + Args: + request (google.devtools.cloudtrace_v1.types.ListTracesRequest): + The request object. The request message for the + `ListTraces` method. All fields are required unless + specified. + project_id (str): + Required. ID of the Cloud project + where the trace data is stored. + + This corresponds to the ``project_id`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.devtools.cloudtrace_v1.services.trace_service.pagers.ListTracesPager: + The response message for the ListTraces method. + + Iterating over this object will yield results and + resolve additional pages automatically. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([project_id]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a trace.ListTracesRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, trace.ListTracesRequest): + request = trace.ListTracesRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if project_id is not None: + request.project_id = project_id + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.list_traces] + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # This method is paged; wrap the response in a pager, which provides + # an `__iter__` convenience method. + response = pagers.ListTracesPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + def get_trace( + self, + request: trace.GetTraceRequest = None, + *, + project_id: str = None, + trace_id: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> trace.Trace: + r"""Gets a single trace by its ID. + + Args: + request (google.devtools.cloudtrace_v1.types.GetTraceRequest): + The request object. The request message for the + `GetTrace` method. + project_id (str): + Required. ID of the Cloud project + where the trace data is stored. + + This corresponds to the ``project_id`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + trace_id (str): + Required. ID of the trace to return. + This corresponds to the ``trace_id`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.devtools.cloudtrace_v1.types.Trace: + A trace describes how long it takes + for an application to perform an + operation. It consists of a set of + spans, each of which represent a single + timed event within the operation. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([project_id, trace_id]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a trace.GetTraceRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, trace.GetTraceRequest): + request = trace.GetTraceRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if project_id is not None: + request.project_id = project_id + if trace_id is not None: + request.trace_id = trace_id + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.get_trace] + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def patch_traces( + self, + request: trace.PatchTracesRequest = None, + *, + project_id: str = None, + traces: trace.Traces = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> None: + r"""Sends new traces to Stackdriver Trace or updates + existing traces. If the ID of a trace that you send + matches that of an existing trace, any fields in the + existing trace and its spans are overwritten by the + provided values, and any new fields provided are merged + with the existing trace data. If the ID does not match, + a new trace is created. + + Args: + request (google.devtools.cloudtrace_v1.types.PatchTracesRequest): + The request object. The request message for the + `PatchTraces` method. + project_id (str): + Required. ID of the Cloud project + where the trace data is stored. + + This corresponds to the ``project_id`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + traces (google.devtools.cloudtrace_v1.types.Traces): + Required. The body of the message. + This corresponds to the ``traces`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([project_id, traces]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a trace.PatchTracesRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, trace.PatchTracesRequest): + request = trace.PatchTracesRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if project_id is not None: + request.project_id = project_id + if traces is not None: + request.traces = traces + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.patch_traces] + + # Send the request. + rpc( + request, retry=retry, timeout=timeout, metadata=metadata, + ) + + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-devtools-cloudtrace", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("TraceServiceClient",) diff --git a/google/devtools/cloudtrace_v1/services/trace_service/pagers.py b/google/devtools/cloudtrace_v1/services/trace_service/pagers.py new file mode 100644 index 00000000..568691da --- /dev/null +++ b/google/devtools/cloudtrace_v1/services/trace_service/pagers.py @@ -0,0 +1,157 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 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. +# + +from typing import ( + Any, + AsyncIterable, + Awaitable, + Callable, + Iterable, + Sequence, + Tuple, + Optional, +) + +from google.devtools.cloudtrace_v1.types import trace + + +class ListTracesPager: + """A pager for iterating through ``list_traces`` requests. + + This class thinly wraps an initial + :class:`google.devtools.cloudtrace_v1.types.ListTracesResponse` object, and + provides an ``__iter__`` method to iterate through its + ``traces`` field. + + If there are more pages, the ``__iter__`` method will make additional + ``ListTraces`` requests and continue to iterate + through the ``traces`` field on the + corresponding responses. + + All the usual :class:`google.devtools.cloudtrace_v1.types.ListTracesResponse` + attributes are available on the pager. If multiple requests are made, only + the most recent response is retained, and thus used for attribute lookup. + """ + + def __init__( + self, + method: Callable[..., trace.ListTracesResponse], + request: trace.ListTracesRequest, + response: trace.ListTracesResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.devtools.cloudtrace_v1.types.ListTracesRequest): + The initial request object. + response (google.devtools.cloudtrace_v1.types.ListTracesResponse): + The initial response object. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + """ + self._method = method + self._request = trace.ListTracesRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + def pages(self) -> Iterable[trace.ListTracesResponse]: + yield self._response + while self._response.next_page_token: + self._request.page_token = self._response.next_page_token + self._response = self._method(self._request, metadata=self._metadata) + yield self._response + + def __iter__(self) -> Iterable[trace.Trace]: + for page in self.pages: + yield from page.traces + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class ListTracesAsyncPager: + """A pager for iterating through ``list_traces`` requests. + + This class thinly wraps an initial + :class:`google.devtools.cloudtrace_v1.types.ListTracesResponse` object, and + provides an ``__aiter__`` method to iterate through its + ``traces`` field. + + If there are more pages, the ``__aiter__`` method will make additional + ``ListTraces`` requests and continue to iterate + through the ``traces`` field on the + corresponding responses. + + All the usual :class:`google.devtools.cloudtrace_v1.types.ListTracesResponse` + attributes are available on the pager. If multiple requests are made, only + the most recent response is retained, and thus used for attribute lookup. + """ + + def __init__( + self, + method: Callable[..., Awaitable[trace.ListTracesResponse]], + request: trace.ListTracesRequest, + response: trace.ListTracesResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.devtools.cloudtrace_v1.types.ListTracesRequest): + The initial request object. + response (google.devtools.cloudtrace_v1.types.ListTracesResponse): + The initial response object. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + """ + self._method = method + self._request = trace.ListTracesRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + async def pages(self) -> AsyncIterable[trace.ListTracesResponse]: + yield self._response + while self._response.next_page_token: + self._request.page_token = self._response.next_page_token + self._response = await self._method(self._request, metadata=self._metadata) + yield self._response + + def __aiter__(self) -> AsyncIterable[trace.Trace]: + async def async_generator(): + async for page in self.pages: + for response in page.traces: + yield response + + return async_generator() + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) diff --git a/google/devtools/cloudtrace_v1/services/trace_service/transports/__init__.py b/google/devtools/cloudtrace_v1/services/trace_service/transports/__init__.py new file mode 100644 index 00000000..b860866b --- /dev/null +++ b/google/devtools/cloudtrace_v1/services/trace_service/transports/__init__.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 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. +# + +from collections import OrderedDict +from typing import Dict, Type + +from .base import TraceServiceTransport +from .grpc import TraceServiceGrpcTransport +from .grpc_asyncio import TraceServiceGrpcAsyncIOTransport + + +# Compile a registry of transports. +_transport_registry = OrderedDict() # type: Dict[str, Type[TraceServiceTransport]] +_transport_registry["grpc"] = TraceServiceGrpcTransport +_transport_registry["grpc_asyncio"] = TraceServiceGrpcAsyncIOTransport + +__all__ = ( + "TraceServiceTransport", + "TraceServiceGrpcTransport", + "TraceServiceGrpcAsyncIOTransport", +) diff --git a/google/devtools/cloudtrace_v1/services/trace_service/transports/base.py b/google/devtools/cloudtrace_v1/services/trace_service/transports/base.py new file mode 100644 index 00000000..efc90795 --- /dev/null +++ b/google/devtools/cloudtrace_v1/services/trace_service/transports/base.py @@ -0,0 +1,188 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 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. +# + +import abc +import typing +import pkg_resources + +from google import auth # type: ignore +from google.api_core import exceptions # type: ignore +from google.api_core import gapic_v1 # type: ignore +from google.api_core import retry as retries # type: ignore +from google.auth import credentials # type: ignore + +from google.devtools.cloudtrace_v1.types import trace +from google.protobuf import empty_pb2 as empty # type: ignore + + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-devtools-cloudtrace", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +class TraceServiceTransport(abc.ABC): + """Abstract transport class for TraceService.""" + + AUTH_SCOPES = ( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/trace.append", + "https://www.googleapis.com/auth/trace.readonly", + ) + + def __init__( + self, + *, + host: str = "cloudtrace.googleapis.com", + credentials: credentials.Credentials = None, + credentials_file: typing.Optional[str] = None, + scopes: typing.Optional[typing.Sequence[str]] = AUTH_SCOPES, + quota_project_id: typing.Optional[str] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + **kwargs, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is mutually exclusive with credentials. + scope (Optional[Sequence[str]]): A list of scopes. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + """ + # Save the hostname. Default to port 443 (HTTPS) if none is specified. + if ":" not in host: + host += ":443" + self._host = host + + # Save the scopes. + self._scopes = scopes or self.AUTH_SCOPES + + # If no credentials are provided, then determine the appropriate + # defaults. + if credentials and credentials_file: + raise exceptions.DuplicateCredentialArgs( + "'credentials_file' and 'credentials' are mutually exclusive" + ) + + if credentials_file is not None: + credentials, _ = auth.load_credentials_from_file( + credentials_file, scopes=self._scopes, quota_project_id=quota_project_id + ) + + elif credentials is None: + credentials, _ = auth.default( + scopes=self._scopes, quota_project_id=quota_project_id + ) + + # Save the credentials. + self._credentials = credentials + + def _prep_wrapped_messages(self, client_info): + # Precompute the wrapped methods. + self._wrapped_methods = { + self.list_traces: gapic_v1.method.wrap_method( + self.list_traces, + default_retry=retries.Retry( + initial=0.1, + maximum=1.0, + multiplier=1.2, + predicate=retries.if_exception_type( + exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, + ), + deadline=45.0, + ), + default_timeout=45.0, + client_info=client_info, + ), + self.get_trace: gapic_v1.method.wrap_method( + self.get_trace, + default_retry=retries.Retry( + initial=0.1, + maximum=1.0, + multiplier=1.2, + predicate=retries.if_exception_type( + exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, + ), + deadline=45.0, + ), + default_timeout=45.0, + client_info=client_info, + ), + self.patch_traces: gapic_v1.method.wrap_method( + self.patch_traces, + default_retry=retries.Retry( + initial=0.1, + maximum=1.0, + multiplier=1.2, + predicate=retries.if_exception_type( + exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, + ), + deadline=45.0, + ), + default_timeout=45.0, + client_info=client_info, + ), + } + + @property + def list_traces( + self, + ) -> typing.Callable[ + [trace.ListTracesRequest], + typing.Union[ + trace.ListTracesResponse, typing.Awaitable[trace.ListTracesResponse] + ], + ]: + raise NotImplementedError() + + @property + def get_trace( + self, + ) -> typing.Callable[ + [trace.GetTraceRequest], + typing.Union[trace.Trace, typing.Awaitable[trace.Trace]], + ]: + raise NotImplementedError() + + @property + def patch_traces( + self, + ) -> typing.Callable[ + [trace.PatchTracesRequest], + typing.Union[empty.Empty, typing.Awaitable[empty.Empty]], + ]: + raise NotImplementedError() + + +__all__ = ("TraceServiceTransport",) diff --git a/google/devtools/cloudtrace_v1/services/trace_service/transports/grpc.py b/google/devtools/cloudtrace_v1/services/trace_service/transports/grpc.py new file mode 100644 index 00000000..458dd49c --- /dev/null +++ b/google/devtools/cloudtrace_v1/services/trace_service/transports/grpc.py @@ -0,0 +1,312 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 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. +# + +import warnings +from typing import Callable, Dict, Optional, Sequence, Tuple + +from google.api_core import grpc_helpers # type: ignore +from google.api_core import gapic_v1 # type: ignore +from google import auth # type: ignore +from google.auth import credentials # type: ignore +from google.auth.transport.grpc import SslCredentials # type: ignore + +import grpc # type: ignore + +from google.devtools.cloudtrace_v1.types import trace +from google.protobuf import empty_pb2 as empty # type: ignore + +from .base import TraceServiceTransport, DEFAULT_CLIENT_INFO + + +class TraceServiceGrpcTransport(TraceServiceTransport): + """gRPC backend transport for TraceService. + + This file describes an API for collecting and viewing traces + and spans within a trace. A Trace is a collection of spans + corresponding to a single operation or set of operations for an + application. A span is an individual timed event which forms a + node of the trace tree. Spans for a single trace may span + multiple services. + + This class defines the same methods as the primary client, so the + primary client can load the underlying transport implementation + and call it. + + It sends protocol buffers over the wire using gRPC (which is built on + top of HTTP/2); the ``grpcio`` package must be installed. + """ + + _stubs: Dict[str, Callable] + + def __init__( + self, + *, + host: str = "cloudtrace.googleapis.com", + credentials: credentials.Credentials = None, + credentials_file: str = None, + scopes: Sequence[str] = None, + channel: grpc.Channel = None, + api_mtls_endpoint: str = None, + client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, + quota_project_id: Optional[str] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + This argument is ignored if ``channel`` is provided. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional(Sequence[str])): A list of scopes. This argument is + ignored if ``channel`` is provided. + channel (Optional[grpc.Channel]): A ``Channel`` instance through + which to make calls. + api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. + If provided, it overrides the ``host`` argument and tries to create + a mutual TLS channel with client SSL credentials from + ``client_cert_source`` or applicatin default SSL credentials. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): + Deprecated. A callback to provide client SSL certificate bytes and + private key bytes, both in PEM format. It is ignored if + ``api_mtls_endpoint`` is None. + ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials + for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + + Raises: + google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport + creation failed for any reason. + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + self._grpc_channel = None + self._ssl_channel_credentials = ssl_channel_credentials + self._stubs: Dict[str, Callable] = {} + + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + + if channel: + # Ignore credentials if a channel was passed. + credentials = False + # If a channel was explicitly provided, set it. + self._grpc_channel = channel + self._ssl_channel_credentials = None + + else: + if api_mtls_endpoint: + host = api_mtls_endpoint + + # Create SSL credentials with client_cert_source or application + # default SSL credentials. + if client_cert_source: + cert, key = client_cert_source() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + else: + self._ssl_channel_credentials = SslCredentials().ssl_credentials + + else: + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + + # The base transport sets the host, credentials and scopes + super().__init__( + host=host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, + client_info=client_info, + ) + + if not self._grpc_channel: + self._grpc_channel = type(self).create_channel( + self._host, + credentials=self._credentials, + credentials_file=credentials_file, + scopes=self._scopes, + ssl_credentials=self._ssl_channel_credentials, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Wrap messages. This must be done after self._grpc_channel exists + self._prep_wrapped_messages(client_info) + + @classmethod + def create_channel( + cls, + host: str = "cloudtrace.googleapis.com", + credentials: credentials.Credentials = None, + credentials_file: str = None, + scopes: Optional[Sequence[str]] = None, + quota_project_id: Optional[str] = None, + **kwargs, + ) -> grpc.Channel: + """Create and return a gRPC channel object. + Args: + host (Optional[str]): The host for the channel to use. + credentials (Optional[~.Credentials]): The + authorization credentials to attach to requests. These + credentials identify this application to the service. If + none are specified, the client will attempt to ascertain + the credentials from the environment. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is mutually exclusive with credentials. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + kwargs (Optional[dict]): Keyword arguments, which are passed to the + channel creation. + Returns: + grpc.Channel: A gRPC channel object. + + Raises: + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + scopes = scopes or cls.AUTH_SCOPES + return grpc_helpers.create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, + **kwargs, + ) + + @property + def grpc_channel(self) -> grpc.Channel: + """Return the channel designed to connect to this service. + """ + return self._grpc_channel + + @property + def list_traces( + self, + ) -> Callable[[trace.ListTracesRequest], trace.ListTracesResponse]: + r"""Return a callable for the list traces method over gRPC. + + Returns of a list of traces that match the specified + filter conditions. + + Returns: + Callable[[~.ListTracesRequest], + ~.ListTracesResponse]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "list_traces" not in self._stubs: + self._stubs["list_traces"] = self.grpc_channel.unary_unary( + "/google.devtools.cloudtrace.v1.TraceService/ListTraces", + request_serializer=trace.ListTracesRequest.serialize, + response_deserializer=trace.ListTracesResponse.deserialize, + ) + return self._stubs["list_traces"] + + @property + def get_trace(self) -> Callable[[trace.GetTraceRequest], trace.Trace]: + r"""Return a callable for the get trace method over gRPC. + + Gets a single trace by its ID. + + Returns: + Callable[[~.GetTraceRequest], + ~.Trace]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "get_trace" not in self._stubs: + self._stubs["get_trace"] = self.grpc_channel.unary_unary( + "/google.devtools.cloudtrace.v1.TraceService/GetTrace", + request_serializer=trace.GetTraceRequest.serialize, + response_deserializer=trace.Trace.deserialize, + ) + return self._stubs["get_trace"] + + @property + def patch_traces(self) -> Callable[[trace.PatchTracesRequest], empty.Empty]: + r"""Return a callable for the patch traces method over gRPC. + + Sends new traces to Stackdriver Trace or updates + existing traces. If the ID of a trace that you send + matches that of an existing trace, any fields in the + existing trace and its spans are overwritten by the + provided values, and any new fields provided are merged + with the existing trace data. If the ID does not match, + a new trace is created. + + Returns: + Callable[[~.PatchTracesRequest], + ~.Empty]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "patch_traces" not in self._stubs: + self._stubs["patch_traces"] = self.grpc_channel.unary_unary( + "/google.devtools.cloudtrace.v1.TraceService/PatchTraces", + request_serializer=trace.PatchTracesRequest.serialize, + response_deserializer=empty.Empty.FromString, + ) + return self._stubs["patch_traces"] + + +__all__ = ("TraceServiceGrpcTransport",) diff --git a/google/devtools/cloudtrace_v1/services/trace_service/transports/grpc_asyncio.py b/google/devtools/cloudtrace_v1/services/trace_service/transports/grpc_asyncio.py new file mode 100644 index 00000000..c13c1125 --- /dev/null +++ b/google/devtools/cloudtrace_v1/services/trace_service/transports/grpc_asyncio.py @@ -0,0 +1,318 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 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. +# + +import warnings +from typing import Awaitable, Callable, Dict, Optional, Sequence, Tuple + +from google.api_core import gapic_v1 # type: ignore +from google.api_core import grpc_helpers_async # type: ignore +from google import auth # type: ignore +from google.auth import credentials # type: ignore +from google.auth.transport.grpc import SslCredentials # type: ignore + +import grpc # type: ignore +from grpc.experimental import aio # type: ignore + +from google.devtools.cloudtrace_v1.types import trace +from google.protobuf import empty_pb2 as empty # type: ignore + +from .base import TraceServiceTransport, DEFAULT_CLIENT_INFO +from .grpc import TraceServiceGrpcTransport + + +class TraceServiceGrpcAsyncIOTransport(TraceServiceTransport): + """gRPC AsyncIO backend transport for TraceService. + + This file describes an API for collecting and viewing traces + and spans within a trace. A Trace is a collection of spans + corresponding to a single operation or set of operations for an + application. A span is an individual timed event which forms a + node of the trace tree. Spans for a single trace may span + multiple services. + + This class defines the same methods as the primary client, so the + primary client can load the underlying transport implementation + and call it. + + It sends protocol buffers over the wire using gRPC (which is built on + top of HTTP/2); the ``grpcio`` package must be installed. + """ + + _grpc_channel: aio.Channel + _stubs: Dict[str, Callable] = {} + + @classmethod + def create_channel( + cls, + host: str = "cloudtrace.googleapis.com", + credentials: credentials.Credentials = None, + credentials_file: Optional[str] = None, + scopes: Optional[Sequence[str]] = None, + quota_project_id: Optional[str] = None, + **kwargs, + ) -> aio.Channel: + """Create and return a gRPC AsyncIO channel object. + Args: + host (Optional[str]): The host for the channel to use. + credentials (Optional[~.Credentials]): The + authorization credentials to attach to requests. These + credentials identify this application to the service. If + none are specified, the client will attempt to ascertain + the credentials from the environment. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + kwargs (Optional[dict]): Keyword arguments, which are passed to the + channel creation. + Returns: + aio.Channel: A gRPC AsyncIO channel object. + """ + scopes = scopes or cls.AUTH_SCOPES + return grpc_helpers_async.create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, + **kwargs, + ) + + def __init__( + self, + *, + host: str = "cloudtrace.googleapis.com", + credentials: credentials.Credentials = None, + credentials_file: Optional[str] = None, + scopes: Optional[Sequence[str]] = None, + channel: aio.Channel = None, + api_mtls_endpoint: str = None, + client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, + quota_project_id=None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + This argument is ignored if ``channel`` is provided. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + channel (Optional[aio.Channel]): A ``Channel`` instance through + which to make calls. + api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. + If provided, it overrides the ``host`` argument and tries to create + a mutual TLS channel with client SSL credentials from + ``client_cert_source`` or applicatin default SSL credentials. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): + Deprecated. A callback to provide client SSL certificate bytes and + private key bytes, both in PEM format. It is ignored if + ``api_mtls_endpoint`` is None. + ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials + for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + + Raises: + google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport + creation failed for any reason. + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + self._grpc_channel = None + self._ssl_channel_credentials = ssl_channel_credentials + self._stubs: Dict[str, Callable] = {} + + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + + if channel: + # Ignore credentials if a channel was passed. + credentials = False + # If a channel was explicitly provided, set it. + self._grpc_channel = channel + self._ssl_channel_credentials = None + + else: + if api_mtls_endpoint: + host = api_mtls_endpoint + + # Create SSL credentials with client_cert_source or application + # default SSL credentials. + if client_cert_source: + cert, key = client_cert_source() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + else: + self._ssl_channel_credentials = SslCredentials().ssl_credentials + + else: + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + + # The base transport sets the host, credentials and scopes + super().__init__( + host=host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, + client_info=client_info, + ) + + if not self._grpc_channel: + self._grpc_channel = type(self).create_channel( + self._host, + credentials=self._credentials, + credentials_file=credentials_file, + scopes=self._scopes, + ssl_credentials=self._ssl_channel_credentials, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Wrap messages. This must be done after self._grpc_channel exists + self._prep_wrapped_messages(client_info) + + @property + def grpc_channel(self) -> aio.Channel: + """Create the channel designed to connect to this service. + + This property caches on the instance; repeated calls return + the same channel. + """ + # Return the channel from cache. + return self._grpc_channel + + @property + def list_traces( + self, + ) -> Callable[[trace.ListTracesRequest], Awaitable[trace.ListTracesResponse]]: + r"""Return a callable for the list traces method over gRPC. + + Returns of a list of traces that match the specified + filter conditions. + + Returns: + Callable[[~.ListTracesRequest], + Awaitable[~.ListTracesResponse]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "list_traces" not in self._stubs: + self._stubs["list_traces"] = self.grpc_channel.unary_unary( + "/google.devtools.cloudtrace.v1.TraceService/ListTraces", + request_serializer=trace.ListTracesRequest.serialize, + response_deserializer=trace.ListTracesResponse.deserialize, + ) + return self._stubs["list_traces"] + + @property + def get_trace(self) -> Callable[[trace.GetTraceRequest], Awaitable[trace.Trace]]: + r"""Return a callable for the get trace method over gRPC. + + Gets a single trace by its ID. + + Returns: + Callable[[~.GetTraceRequest], + Awaitable[~.Trace]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "get_trace" not in self._stubs: + self._stubs["get_trace"] = self.grpc_channel.unary_unary( + "/google.devtools.cloudtrace.v1.TraceService/GetTrace", + request_serializer=trace.GetTraceRequest.serialize, + response_deserializer=trace.Trace.deserialize, + ) + return self._stubs["get_trace"] + + @property + def patch_traces( + self, + ) -> Callable[[trace.PatchTracesRequest], Awaitable[empty.Empty]]: + r"""Return a callable for the patch traces method over gRPC. + + Sends new traces to Stackdriver Trace or updates + existing traces. If the ID of a trace that you send + matches that of an existing trace, any fields in the + existing trace and its spans are overwritten by the + provided values, and any new fields provided are merged + with the existing trace data. If the ID does not match, + a new trace is created. + + Returns: + Callable[[~.PatchTracesRequest], + Awaitable[~.Empty]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "patch_traces" not in self._stubs: + self._stubs["patch_traces"] = self.grpc_channel.unary_unary( + "/google.devtools.cloudtrace.v1.TraceService/PatchTraces", + request_serializer=trace.PatchTracesRequest.serialize, + response_deserializer=empty.Empty.FromString, + ) + return self._stubs["patch_traces"] + + +__all__ = ("TraceServiceGrpcAsyncIOTransport",) diff --git a/google/devtools/cloudtrace_v1/types/__init__.py b/google/devtools/cloudtrace_v1/types/__init__.py new file mode 100644 index 00000000..540e3e00 --- /dev/null +++ b/google/devtools/cloudtrace_v1/types/__init__.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 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. +# + +from .trace import ( + GetTraceRequest, + ListTracesRequest, + ListTracesResponse, + PatchTracesRequest, + Trace, + Traces, + TraceSpan, +) + +__all__ = ( + "GetTraceRequest", + "ListTracesRequest", + "ListTracesResponse", + "PatchTracesRequest", + "Trace", + "Traces", + "TraceSpan", +) diff --git a/google/devtools/cloudtrace_v1/types/trace.py b/google/devtools/cloudtrace_v1/types/trace.py new file mode 100644 index 00000000..1ef48db2 --- /dev/null +++ b/google/devtools/cloudtrace_v1/types/trace.py @@ -0,0 +1,329 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 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. +# + +import proto # type: ignore + + +from google.protobuf import timestamp_pb2 as timestamp # type: ignore + + +__protobuf__ = proto.module( + package="google.devtools.cloudtrace.v1", + manifest={ + "Trace", + "Traces", + "TraceSpan", + "ListTracesRequest", + "ListTracesResponse", + "GetTraceRequest", + "PatchTracesRequest", + }, +) + + +class Trace(proto.Message): + r"""A trace describes how long it takes for an application to + perform an operation. It consists of a set of spans, each of + which represent a single timed event within the operation. + + Attributes: + project_id (str): + Project ID of the Cloud project where the + trace data is stored. + trace_id (str): + Globally unique identifier for the trace. This identifier is + a 128-bit numeric value formatted as a 32-byte hex string. + For example, ``382d4f4c6b7bb2f4a972559d9085001d``. + spans (Sequence[google.devtools.cloudtrace_v1.types.TraceSpan]): + Collection of spans in the trace. + """ + + project_id = proto.Field(proto.STRING, number=1) + + trace_id = proto.Field(proto.STRING, number=2) + + spans = proto.RepeatedField(proto.MESSAGE, number=3, message="TraceSpan",) + + +class Traces(proto.Message): + r"""List of new or updated traces. + + Attributes: + traces (Sequence[google.devtools.cloudtrace_v1.types.Trace]): + List of traces. + """ + + traces = proto.RepeatedField(proto.MESSAGE, number=1, message="Trace",) + + +class TraceSpan(proto.Message): + r"""A span represents a single timed event within a trace. Spans + can be nested and form a trace tree. Often, a trace contains a + root span that describes the end-to-end latency of an operation + and, optionally, one or more subspans for its suboperations. + Spans do not need to be contiguous. There may be gaps between + spans in a trace. + + Attributes: + span_id (int): + Identifier for the span. Must be a 64-bit integer other than + 0 and unique within a trace. For example, + ``2205310701640571284``. + kind (google.devtools.cloudtrace_v1.types.TraceSpan.SpanKind): + Distinguishes between spans generated in a particular + context. For example, two spans with the same name may be + distinguished using ``RPC_CLIENT`` and ``RPC_SERVER`` to + identify queueing latency associated with the span. + name (str): + Name of the span. Must be less than 128 + bytes. The span name is sanitized and displayed + in the Stackdriver Trace tool in the Google + Cloud Platform Console. + The name may be a method name or some other per- + call site name. For the same executable and the + same call point, a best practice is to use a + consistent name, which makes it easier to + correlate cross-trace spans. + start_time (google.protobuf.timestamp_pb2.Timestamp): + Start time of the span in nanoseconds from + the UNIX epoch. + end_time (google.protobuf.timestamp_pb2.Timestamp): + End time of the span in nanoseconds from the + UNIX epoch. + parent_span_id (int): + Optional. ID of the parent span, if any. + labels (Sequence[google.devtools.cloudtrace_v1.types.TraceSpan.LabelsEntry]): + Collection of labels associated with the span. Label keys + must be less than 128 bytes. Label values must be less than + 16 kilobytes (10MB for ``/stacktrace`` values). + + Some predefined label keys exist, or you may create your + own. When creating your own, we recommend the following + formats: + + - ``/category/product/key`` for agents of well-known + products (e.g. ``/db/mongodb/read_size``). + - ``short_host/path/key`` for domain-specific keys (e.g. + ``foo.com/myproduct/bar``) + + Predefined labels include: + + - ``/agent`` + - ``/component`` + - ``/error/message`` + - ``/error/name`` + - ``/http/client_city`` + - ``/http/client_country`` + - ``/http/client_protocol`` + - ``/http/client_region`` + - ``/http/host`` + - ``/http/method`` + - ``/http/path`` + - ``/http/redirected_url`` + - ``/http/request/size`` + - ``/http/response/size`` + - ``/http/route`` + - ``/http/status_code`` + - ``/http/url`` + - ``/http/user_agent`` + - ``/pid`` + - ``/stacktrace`` + - ``/tid`` + """ + + class SpanKind(proto.Enum): + r"""Type of span. Can be used to specify additional relationships + between spans in addition to a parent/child relationship. + """ + SPAN_KIND_UNSPECIFIED = 0 + RPC_SERVER = 1 + RPC_CLIENT = 2 + + span_id = proto.Field(proto.FIXED64, number=1) + + kind = proto.Field(proto.ENUM, number=2, enum=SpanKind,) + + name = proto.Field(proto.STRING, number=3) + + start_time = proto.Field(proto.MESSAGE, number=4, message=timestamp.Timestamp,) + + end_time = proto.Field(proto.MESSAGE, number=5, message=timestamp.Timestamp,) + + parent_span_id = proto.Field(proto.FIXED64, number=6) + + labels = proto.MapField(proto.STRING, proto.STRING, number=7) + + +class ListTracesRequest(proto.Message): + r"""The request message for the ``ListTraces`` method. All fields are + required unless specified. + + Attributes: + project_id (str): + Required. ID of the Cloud project where the + trace data is stored. + view (google.devtools.cloudtrace_v1.types.ListTracesRequest.ViewType): + Optional. Type of data returned for traces in the list. + Default is ``MINIMAL``. + page_size (int): + Optional. Maximum number of traces to return. + If not specified or <= 0, the implementation + selects a reasonable value. The implementation + may return fewer traces than the requested page + size. + page_token (str): + Token identifying the page of results to return. If + provided, use the value of the ``next_page_token`` field + from a previous request. + start_time (google.protobuf.timestamp_pb2.Timestamp): + Start of the time interval (inclusive) during + which the trace data was collected from the + application. + end_time (google.protobuf.timestamp_pb2.Timestamp): + End of the time interval (inclusive) during + which the trace data was collected from the + application. + filter (str): + Optional. A filter against labels for the request. + + By default, searches use prefix matching. To specify exact + match, prepend a plus symbol (``+``) to the search term. + Multiple terms are ANDed. Syntax: + + - ``root:NAME_PREFIX`` or ``NAME_PREFIX``: Return traces + where any root span starts with ``NAME_PREFIX``. + - ``+root:NAME`` or ``+NAME``: Return traces where any root + span's name is exactly ``NAME``. + - ``span:NAME_PREFIX``: Return traces where any span starts + with ``NAME_PREFIX``. + - ``+span:NAME``: Return traces where any span's name is + exactly ``NAME``. + - ``latency:DURATION``: Return traces whose overall latency + is greater or equal to than ``DURATION``. Accepted units + are nanoseconds (``ns``), milliseconds (``ms``), and + seconds (``s``). Default is ``ms``. For example, + ``latency:24ms`` returns traces whose overall latency is + greater than or equal to 24 milliseconds. + - ``label:LABEL_KEY``: Return all traces containing the + specified label key (exact match, case-sensitive) + regardless of the key:value pair's value (including empty + values). + - ``LABEL_KEY:VALUE_PREFIX``: Return all traces containing + the specified label key (exact match, case-sensitive) + whose value starts with ``VALUE_PREFIX``. Both a key and + a value must be specified. + - ``+LABEL_KEY:VALUE``: Return all traces containing a + key:value pair exactly matching the specified text. Both + a key and a value must be specified. + - ``method:VALUE``: Equivalent to ``/http/method:VALUE``. + - ``url:VALUE``: Equivalent to ``/http/url:VALUE``. + order_by (str): + Optional. Field used to sort the returned traces. Can be one + of the following: + + - ``trace_id`` + - ``name`` (``name`` field of root span in the trace) + - ``duration`` (difference between ``end_time`` and + ``start_time`` fields of the root span) + - ``start`` (``start_time`` field of the root span) + + Descending order can be specified by appending ``desc`` to + the sort field (for example, ``name desc``). + + Only one sort field is permitted. + """ + + class ViewType(proto.Enum): + r"""Type of data returned for traces in the list.""" + VIEW_TYPE_UNSPECIFIED = 0 + MINIMAL = 1 + ROOTSPAN = 2 + COMPLETE = 3 + + project_id = proto.Field(proto.STRING, number=1) + + view = proto.Field(proto.ENUM, number=2, enum=ViewType,) + + page_size = proto.Field(proto.INT32, number=3) + + page_token = proto.Field(proto.STRING, number=4) + + start_time = proto.Field(proto.MESSAGE, number=5, message=timestamp.Timestamp,) + + end_time = proto.Field(proto.MESSAGE, number=6, message=timestamp.Timestamp,) + + filter = proto.Field(proto.STRING, number=7) + + order_by = proto.Field(proto.STRING, number=8) + + +class ListTracesResponse(proto.Message): + r"""The response message for the ``ListTraces`` method. + + Attributes: + traces (Sequence[google.devtools.cloudtrace_v1.types.Trace]): + List of trace records as specified by the + view parameter. + next_page_token (str): + If defined, indicates that there are more + traces that match the request and that this + value should be passed to the next request to + continue retrieving additional traces. + """ + + @property + def raw_page(self): + return self + + traces = proto.RepeatedField(proto.MESSAGE, number=1, message="Trace",) + + next_page_token = proto.Field(proto.STRING, number=2) + + +class GetTraceRequest(proto.Message): + r"""The request message for the ``GetTrace`` method. + + Attributes: + project_id (str): + Required. ID of the Cloud project where the + trace data is stored. + trace_id (str): + Required. ID of the trace to return. + """ + + project_id = proto.Field(proto.STRING, number=1) + + trace_id = proto.Field(proto.STRING, number=2) + + +class PatchTracesRequest(proto.Message): + r"""The request message for the ``PatchTraces`` method. + + Attributes: + project_id (str): + Required. ID of the Cloud project where the + trace data is stored. + traces (google.devtools.cloudtrace_v1.types.Traces): + Required. The body of the message. + """ + + project_id = proto.Field(proto.STRING, number=1) + + traces = proto.Field(proto.MESSAGE, number=2, message="Traces",) + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/trace_v2/__init__.py b/google/devtools/cloudtrace_v2/__init__.py similarity index 100% rename from google/cloud/trace_v2/__init__.py rename to google/devtools/cloudtrace_v2/__init__.py diff --git a/google/devtools/cloudtrace_v2/py.typed b/google/devtools/cloudtrace_v2/py.typed new file mode 100644 index 00000000..c9f021ea --- /dev/null +++ b/google/devtools/cloudtrace_v2/py.typed @@ -0,0 +1,2 @@ +# Marker file for PEP 561. +# The google-devtools-cloudtrace package uses inline types. diff --git a/google/devtools/cloudtrace_v2/services/__init__.py b/google/devtools/cloudtrace_v2/services/__init__.py new file mode 100644 index 00000000..42ffdf2b --- /dev/null +++ b/google/devtools/cloudtrace_v2/services/__init__.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 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. +# diff --git a/google/devtools/cloudtrace_v2/services/trace_service/__init__.py b/google/devtools/cloudtrace_v2/services/trace_service/__init__.py new file mode 100644 index 00000000..e06e796c --- /dev/null +++ b/google/devtools/cloudtrace_v2/services/trace_service/__init__.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 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. +# + +from .client import TraceServiceClient +from .async_client import TraceServiceAsyncClient + +__all__ = ( + "TraceServiceClient", + "TraceServiceAsyncClient", +) diff --git a/google/devtools/cloudtrace_v2/services/trace_service/async_client.py b/google/devtools/cloudtrace_v2/services/trace_service/async_client.py new file mode 100644 index 00000000..2f60b822 --- /dev/null +++ b/google/devtools/cloudtrace_v2/services/trace_service/async_client.py @@ -0,0 +1,341 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 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. +# + +from collections import OrderedDict +import functools +import re +from typing import Dict, Sequence, Tuple, Type, Union +import pkg_resources + +import google.api_core.client_options as ClientOptions # type: ignore +from google.api_core import exceptions # type: ignore +from google.api_core import gapic_v1 # type: ignore +from google.api_core import retry as retries # type: ignore +from google.auth import credentials # type: ignore +from google.oauth2 import service_account # type: ignore + +from google.devtools.cloudtrace_v2.types import trace +from google.devtools.cloudtrace_v2.types import tracing +from google.protobuf import timestamp_pb2 as timestamp # type: ignore +from google.protobuf import wrappers_pb2 as wrappers # type: ignore +from google.rpc import status_pb2 as status # type: ignore + +from .transports.base import TraceServiceTransport, DEFAULT_CLIENT_INFO +from .transports.grpc_asyncio import TraceServiceGrpcAsyncIOTransport +from .client import TraceServiceClient + + +class TraceServiceAsyncClient: + """This file describes an API for collecting and viewing traces + and spans within a trace. A Trace is a collection of spans + corresponding to a single operation or set of operations for an + application. A span is an individual timed event which forms a + node of the trace tree. A single trace may contain span(s) from + multiple services. + """ + + _client: TraceServiceClient + + DEFAULT_ENDPOINT = TraceServiceClient.DEFAULT_ENDPOINT + DEFAULT_MTLS_ENDPOINT = TraceServiceClient.DEFAULT_MTLS_ENDPOINT + + span_path = staticmethod(TraceServiceClient.span_path) + parse_span_path = staticmethod(TraceServiceClient.parse_span_path) + + common_billing_account_path = staticmethod( + TraceServiceClient.common_billing_account_path + ) + parse_common_billing_account_path = staticmethod( + TraceServiceClient.parse_common_billing_account_path + ) + + common_folder_path = staticmethod(TraceServiceClient.common_folder_path) + parse_common_folder_path = staticmethod(TraceServiceClient.parse_common_folder_path) + + common_organization_path = staticmethod(TraceServiceClient.common_organization_path) + parse_common_organization_path = staticmethod( + TraceServiceClient.parse_common_organization_path + ) + + common_project_path = staticmethod(TraceServiceClient.common_project_path) + parse_common_project_path = staticmethod( + TraceServiceClient.parse_common_project_path + ) + + common_location_path = staticmethod(TraceServiceClient.common_location_path) + parse_common_location_path = staticmethod( + TraceServiceClient.parse_common_location_path + ) + + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + TraceServiceAsyncClient: The constructed client. + """ + return TraceServiceClient.from_service_account_info.__func__(TraceServiceAsyncClient, info, *args, **kwargs) # type: ignore + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + TraceServiceAsyncClient: The constructed client. + """ + return TraceServiceClient.from_service_account_file.__func__(TraceServiceAsyncClient, filename, *args, **kwargs) # type: ignore + + from_service_account_json = from_service_account_file + + @property + def transport(self) -> TraceServiceTransport: + """Return the transport used by the client instance. + + Returns: + TraceServiceTransport: The transport used by the client instance. + """ + return self._client.transport + + get_transport_class = functools.partial( + type(TraceServiceClient).get_transport_class, type(TraceServiceClient) + ) + + def __init__( + self, + *, + credentials: credentials.Credentials = None, + transport: Union[str, TraceServiceTransport] = "grpc_asyncio", + client_options: ClientOptions = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the trace service client. + + Args: + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + transport (Union[str, ~.TraceServiceTransport]): The + transport to use. If set to None, a transport is chosen + automatically. + client_options (ClientOptions): Custom options for the client. It + won't take effect if a ``transport`` instance is provided. + (1) The ``api_endpoint`` property can be used to override the + default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT + environment variable can also be used to override the endpoint: + "always" (always use the default mTLS endpoint), "never" (always + use the default regular endpoint) and "auto" (auto switch to the + default mTLS endpoint if client certificate is present, this is + the default value). However, the ``api_endpoint`` property takes + precedence if provided. + (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable + is "true", then the ``client_cert_source`` property can be used + to provide client certificate for mutual TLS transport. If + not provided, the default SSL client certificate will be used if + present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not + set, no client certificate will be used. + + Raises: + google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport + creation failed for any reason. + """ + + self._client = TraceServiceClient( + credentials=credentials, + transport=transport, + client_options=client_options, + client_info=client_info, + ) + + async def batch_write_spans( + self, + request: tracing.BatchWriteSpansRequest = None, + *, + name: str = None, + spans: Sequence[trace.Span] = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> None: + r"""Sends new spans to new or existing traces. You cannot + update existing spans. + + Args: + request (:class:`google.devtools.cloudtrace_v2.types.BatchWriteSpansRequest`): + The request object. The request message for the + `BatchWriteSpans` method. + name (:class:`str`): + Required. The name of the project where the spans + belong. The format is ``projects/[PROJECT_ID]``. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + spans (:class:`Sequence[google.devtools.cloudtrace_v2.types.Span]`): + Required. A list of new spans. The + span names must not match existing + spans, or the results are undefined. + + This corresponds to the ``spans`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([name, spans]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = tracing.BatchWriteSpansRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if name is not None: + request.name = name + + if spans: + request.spans.extend(spans) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.batch_write_spans, + default_timeout=120.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + await rpc( + request, retry=retry, timeout=timeout, metadata=metadata, + ) + + async def create_span( + self, + request: trace.Span = None, + *, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> trace.Span: + r"""Creates a new span. + + Args: + request (:class:`google.devtools.cloudtrace_v2.types.Span`): + The request object. A span represents a single operation + within a trace. Spans can be nested to form a trace + tree. Often, a trace contains a root span that describes + the end-to-end latency, and one or more subspans for its + sub-operations. A trace can also contain multiple root + spans, or none at all. Spans do not need to be + contiguous—there may be gaps or overlaps between + spans in a trace. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.devtools.cloudtrace_v2.types.Span: + A span represents a single operation + within a trace. Spans can be nested to + form a trace tree. Often, a trace + contains a root span that describes the + end-to-end latency, and one or more + subspans for its sub-operations. A trace + can also contain multiple root spans, or + none at all. Spans do not need to be + contiguous—there may be gaps or + overlaps between spans in a trace. + + """ + # Create or coerce a protobuf request object. + + request = trace.Span(request) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.create_span, + default_retry=retries.Retry( + initial=0.1, + maximum=1.0, + multiplier=1.2, + predicate=retries.if_exception_type( + exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, + ), + deadline=120.0, + ), + default_timeout=120.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-devtools-cloudtrace", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("TraceServiceAsyncClient",) diff --git a/google/devtools/cloudtrace_v2/services/trace_service/client.py b/google/devtools/cloudtrace_v2/services/trace_service/client.py new file mode 100644 index 00000000..45762c7f --- /dev/null +++ b/google/devtools/cloudtrace_v2/services/trace_service/client.py @@ -0,0 +1,509 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 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. +# + +from collections import OrderedDict +from distutils import util +import os +import re +from typing import Callable, Dict, Optional, Sequence, Tuple, Type, Union +import pkg_resources + +from google.api_core import client_options as client_options_lib # type: ignore +from google.api_core import exceptions # type: ignore +from google.api_core import gapic_v1 # type: ignore +from google.api_core import retry as retries # type: ignore +from google.auth import credentials # type: ignore +from google.auth.transport import mtls # type: ignore +from google.auth.transport.grpc import SslCredentials # type: ignore +from google.auth.exceptions import MutualTLSChannelError # type: ignore +from google.oauth2 import service_account # type: ignore + +from google.devtools.cloudtrace_v2.types import trace +from google.devtools.cloudtrace_v2.types import tracing +from google.protobuf import timestamp_pb2 as timestamp # type: ignore +from google.protobuf import wrappers_pb2 as wrappers # type: ignore +from google.rpc import status_pb2 as status # type: ignore + +from .transports.base import TraceServiceTransport, DEFAULT_CLIENT_INFO +from .transports.grpc import TraceServiceGrpcTransport +from .transports.grpc_asyncio import TraceServiceGrpcAsyncIOTransport + + +class TraceServiceClientMeta(type): + """Metaclass for the TraceService client. + + This provides class-level methods for building and retrieving + support objects (e.g. transport) without polluting the client instance + objects. + """ + + _transport_registry = OrderedDict() # type: Dict[str, Type[TraceServiceTransport]] + _transport_registry["grpc"] = TraceServiceGrpcTransport + _transport_registry["grpc_asyncio"] = TraceServiceGrpcAsyncIOTransport + + def get_transport_class(cls, label: str = None,) -> Type[TraceServiceTransport]: + """Return an appropriate transport class. + + Args: + label: The name of the desired transport. If none is + provided, then the first transport in the registry is used. + + Returns: + The transport class to use. + """ + # If a specific transport is requested, return that one. + if label: + return cls._transport_registry[label] + + # No transport is requested; return the default (that is, the first one + # in the dictionary). + return next(iter(cls._transport_registry.values())) + + +class TraceServiceClient(metaclass=TraceServiceClientMeta): + """This file describes an API for collecting and viewing traces + and spans within a trace. A Trace is a collection of spans + corresponding to a single operation or set of operations for an + application. A span is an individual timed event which forms a + node of the trace tree. A single trace may contain span(s) from + multiple services. + """ + + @staticmethod + def _get_default_mtls_endpoint(api_endpoint): + """Convert api endpoint to mTLS endpoint. + Convert "*.sandbox.googleapis.com" and "*.googleapis.com" to + "*.mtls.sandbox.googleapis.com" and "*.mtls.googleapis.com" respectively. + Args: + api_endpoint (Optional[str]): the api endpoint to convert. + Returns: + str: converted mTLS api endpoint. + """ + if not api_endpoint: + return api_endpoint + + mtls_endpoint_re = re.compile( + r"(?P[^.]+)(?P\.mtls)?(?P\.sandbox)?(?P\.googleapis\.com)?" + ) + + m = mtls_endpoint_re.match(api_endpoint) + name, mtls, sandbox, googledomain = m.groups() + if mtls or not googledomain: + return api_endpoint + + if sandbox: + return api_endpoint.replace( + "sandbox.googleapis.com", "mtls.sandbox.googleapis.com" + ) + + return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com") + + DEFAULT_ENDPOINT = "cloudtrace.googleapis.com" + DEFAULT_MTLS_ENDPOINT = _get_default_mtls_endpoint.__func__( # type: ignore + DEFAULT_ENDPOINT + ) + + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + TraceServiceClient: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_info(info) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + TraceServiceClient: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_file(filename) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + + from_service_account_json = from_service_account_file + + @property + def transport(self) -> TraceServiceTransport: + """Return the transport used by the client instance. + + Returns: + TraceServiceTransport: The transport used by the client instance. + """ + return self._transport + + @staticmethod + def span_path(project: str, trace: str, span: str,) -> str: + """Return a fully-qualified span string.""" + return "projects/{project}/traces/{trace}/spans/{span}".format( + project=project, trace=trace, span=span, + ) + + @staticmethod + def parse_span_path(path: str) -> Dict[str, str]: + """Parse a span path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/traces/(?P.+?)/spans/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def common_billing_account_path(billing_account: str,) -> str: + """Return a fully-qualified billing_account string.""" + return "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + + @staticmethod + def parse_common_billing_account_path(path: str) -> Dict[str, str]: + """Parse a billing_account path into its component segments.""" + m = re.match(r"^billingAccounts/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_folder_path(folder: str,) -> str: + """Return a fully-qualified folder string.""" + return "folders/{folder}".format(folder=folder,) + + @staticmethod + def parse_common_folder_path(path: str) -> Dict[str, str]: + """Parse a folder path into its component segments.""" + m = re.match(r"^folders/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_organization_path(organization: str,) -> str: + """Return a fully-qualified organization string.""" + return "organizations/{organization}".format(organization=organization,) + + @staticmethod + def parse_common_organization_path(path: str) -> Dict[str, str]: + """Parse a organization path into its component segments.""" + m = re.match(r"^organizations/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_project_path(project: str,) -> str: + """Return a fully-qualified project string.""" + return "projects/{project}".format(project=project,) + + @staticmethod + def parse_common_project_path(path: str) -> Dict[str, str]: + """Parse a project path into its component segments.""" + m = re.match(r"^projects/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_location_path(project: str, location: str,) -> str: + """Return a fully-qualified location string.""" + return "projects/{project}/locations/{location}".format( + project=project, location=location, + ) + + @staticmethod + def parse_common_location_path(path: str) -> Dict[str, str]: + """Parse a location path into its component segments.""" + m = re.match(r"^projects/(?P.+?)/locations/(?P.+?)$", path) + return m.groupdict() if m else {} + + def __init__( + self, + *, + credentials: Optional[credentials.Credentials] = None, + transport: Union[str, TraceServiceTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the trace service client. + + Args: + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + transport (Union[str, TraceServiceTransport]): The + transport to use. If set to None, a transport is chosen + automatically. + client_options (google.api_core.client_options.ClientOptions): Custom options for the + client. It won't take effect if a ``transport`` instance is provided. + (1) The ``api_endpoint`` property can be used to override the + default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT + environment variable can also be used to override the endpoint: + "always" (always use the default mTLS endpoint), "never" (always + use the default regular endpoint) and "auto" (auto switch to the + default mTLS endpoint if client certificate is present, this is + the default value). However, the ``api_endpoint`` property takes + precedence if provided. + (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable + is "true", then the ``client_cert_source`` property can be used + to provide client certificate for mutual TLS transport. If + not provided, the default SSL client certificate will be used if + present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not + set, no client certificate will be used. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + + Raises: + google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport + creation failed for any reason. + """ + if isinstance(client_options, dict): + client_options = client_options_lib.from_dict(client_options) + if client_options is None: + client_options = client_options_lib.ClientOptions() + + # Create SSL credentials for mutual TLS if needed. + use_client_cert = bool( + util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) + ) + + client_cert_source_func = None + is_mtls = False + if use_client_cert: + if client_options.client_cert_source: + is_mtls = True + client_cert_source_func = client_options.client_cert_source + else: + is_mtls = mtls.has_default_client_cert_source() + client_cert_source_func = ( + mtls.default_client_cert_source() if is_mtls else None + ) + + # Figure out which api endpoint to use. + if client_options.api_endpoint is not None: + api_endpoint = client_options.api_endpoint + else: + use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto") + if use_mtls_env == "never": + api_endpoint = self.DEFAULT_ENDPOINT + elif use_mtls_env == "always": + api_endpoint = self.DEFAULT_MTLS_ENDPOINT + elif use_mtls_env == "auto": + api_endpoint = ( + self.DEFAULT_MTLS_ENDPOINT if is_mtls else self.DEFAULT_ENDPOINT + ) + else: + raise MutualTLSChannelError( + "Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted values: never, auto, always" + ) + + # Save or instantiate the transport. + # Ordinarily, we provide the transport, but allowing a custom transport + # instance provides an extensibility point for unusual situations. + if isinstance(transport, TraceServiceTransport): + # transport is a TraceServiceTransport instance. + if credentials or client_options.credentials_file: + raise ValueError( + "When providing a transport instance, " + "provide its credentials directly." + ) + if client_options.scopes: + raise ValueError( + "When providing a transport instance, " + "provide its scopes directly." + ) + self._transport = transport + else: + Transport = type(self).get_transport_class(transport) + self._transport = Transport( + credentials=credentials, + credentials_file=client_options.credentials_file, + host=api_endpoint, + scopes=client_options.scopes, + client_cert_source_for_mtls=client_cert_source_func, + quota_project_id=client_options.quota_project_id, + client_info=client_info, + ) + + def batch_write_spans( + self, + request: tracing.BatchWriteSpansRequest = None, + *, + name: str = None, + spans: Sequence[trace.Span] = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> None: + r"""Sends new spans to new or existing traces. You cannot + update existing spans. + + Args: + request (google.devtools.cloudtrace_v2.types.BatchWriteSpansRequest): + The request object. The request message for the + `BatchWriteSpans` method. + name (str): + Required. The name of the project where the spans + belong. The format is ``projects/[PROJECT_ID]``. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + spans (Sequence[google.devtools.cloudtrace_v2.types.Span]): + Required. A list of new spans. The + span names must not match existing + spans, or the results are undefined. + + This corresponds to the ``spans`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([name, spans]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a tracing.BatchWriteSpansRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, tracing.BatchWriteSpansRequest): + request = tracing.BatchWriteSpansRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if name is not None: + request.name = name + if spans is not None: + request.spans = spans + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.batch_write_spans] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + rpc( + request, retry=retry, timeout=timeout, metadata=metadata, + ) + + def create_span( + self, + request: trace.Span = None, + *, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> trace.Span: + r"""Creates a new span. + + Args: + request (google.devtools.cloudtrace_v2.types.Span): + The request object. A span represents a single operation + within a trace. Spans can be nested to form a trace + tree. Often, a trace contains a root span that describes + the end-to-end latency, and one or more subspans for its + sub-operations. A trace can also contain multiple root + spans, or none at all. Spans do not need to be + contiguous—there may be gaps or overlaps between + spans in a trace. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.devtools.cloudtrace_v2.types.Span: + A span represents a single operation + within a trace. Spans can be nested to + form a trace tree. Often, a trace + contains a root span that describes the + end-to-end latency, and one or more + subspans for its sub-operations. A trace + can also contain multiple root spans, or + none at all. Spans do not need to be + contiguous—there may be gaps or + overlaps between spans in a trace. + + """ + # Create or coerce a protobuf request object. + + # Minor optimization to avoid making a copy if the user passes + # in a trace.Span. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, trace.Span): + request = trace.Span(request) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.create_span] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-devtools-cloudtrace", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("TraceServiceClient",) diff --git a/google/devtools/cloudtrace_v2/services/trace_service/transports/__init__.py b/google/devtools/cloudtrace_v2/services/trace_service/transports/__init__.py new file mode 100644 index 00000000..b860866b --- /dev/null +++ b/google/devtools/cloudtrace_v2/services/trace_service/transports/__init__.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 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. +# + +from collections import OrderedDict +from typing import Dict, Type + +from .base import TraceServiceTransport +from .grpc import TraceServiceGrpcTransport +from .grpc_asyncio import TraceServiceGrpcAsyncIOTransport + + +# Compile a registry of transports. +_transport_registry = OrderedDict() # type: Dict[str, Type[TraceServiceTransport]] +_transport_registry["grpc"] = TraceServiceGrpcTransport +_transport_registry["grpc_asyncio"] = TraceServiceGrpcAsyncIOTransport + +__all__ = ( + "TraceServiceTransport", + "TraceServiceGrpcTransport", + "TraceServiceGrpcAsyncIOTransport", +) diff --git a/google/devtools/cloudtrace_v2/services/trace_service/transports/base.py b/google/devtools/cloudtrace_v2/services/trace_service/transports/base.py new file mode 100644 index 00000000..5a934424 --- /dev/null +++ b/google/devtools/cloudtrace_v2/services/trace_service/transports/base.py @@ -0,0 +1,151 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 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. +# + +import abc +import typing +import pkg_resources + +from google import auth # type: ignore +from google.api_core import exceptions # type: ignore +from google.api_core import gapic_v1 # type: ignore +from google.api_core import retry as retries # type: ignore +from google.auth import credentials # type: ignore + +from google.devtools.cloudtrace_v2.types import trace +from google.devtools.cloudtrace_v2.types import tracing +from google.protobuf import empty_pb2 as empty # type: ignore + + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-devtools-cloudtrace", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +class TraceServiceTransport(abc.ABC): + """Abstract transport class for TraceService.""" + + AUTH_SCOPES = ( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/trace.append", + ) + + def __init__( + self, + *, + host: str = "cloudtrace.googleapis.com", + credentials: credentials.Credentials = None, + credentials_file: typing.Optional[str] = None, + scopes: typing.Optional[typing.Sequence[str]] = AUTH_SCOPES, + quota_project_id: typing.Optional[str] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + **kwargs, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is mutually exclusive with credentials. + scope (Optional[Sequence[str]]): A list of scopes. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + """ + # Save the hostname. Default to port 443 (HTTPS) if none is specified. + if ":" not in host: + host += ":443" + self._host = host + + # Save the scopes. + self._scopes = scopes or self.AUTH_SCOPES + + # If no credentials are provided, then determine the appropriate + # defaults. + if credentials and credentials_file: + raise exceptions.DuplicateCredentialArgs( + "'credentials_file' and 'credentials' are mutually exclusive" + ) + + if credentials_file is not None: + credentials, _ = auth.load_credentials_from_file( + credentials_file, scopes=self._scopes, quota_project_id=quota_project_id + ) + + elif credentials is None: + credentials, _ = auth.default( + scopes=self._scopes, quota_project_id=quota_project_id + ) + + # Save the credentials. + self._credentials = credentials + + def _prep_wrapped_messages(self, client_info): + # Precompute the wrapped methods. + self._wrapped_methods = { + self.batch_write_spans: gapic_v1.method.wrap_method( + self.batch_write_spans, default_timeout=120.0, client_info=client_info, + ), + self.create_span: gapic_v1.method.wrap_method( + self.create_span, + default_retry=retries.Retry( + initial=0.1, + maximum=1.0, + multiplier=1.2, + predicate=retries.if_exception_type( + exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, + ), + deadline=120.0, + ), + default_timeout=120.0, + client_info=client_info, + ), + } + + @property + def batch_write_spans( + self, + ) -> typing.Callable[ + [tracing.BatchWriteSpansRequest], + typing.Union[empty.Empty, typing.Awaitable[empty.Empty]], + ]: + raise NotImplementedError() + + @property + def create_span( + self, + ) -> typing.Callable[ + [trace.Span], typing.Union[trace.Span, typing.Awaitable[trace.Span]] + ]: + raise NotImplementedError() + + +__all__ = ("TraceServiceTransport",) diff --git a/google/devtools/cloudtrace_v2/services/trace_service/transports/grpc.py b/google/devtools/cloudtrace_v2/services/trace_service/transports/grpc.py new file mode 100644 index 00000000..2cd5e147 --- /dev/null +++ b/google/devtools/cloudtrace_v2/services/trace_service/transports/grpc.py @@ -0,0 +1,283 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 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. +# + +import warnings +from typing import Callable, Dict, Optional, Sequence, Tuple + +from google.api_core import grpc_helpers # type: ignore +from google.api_core import gapic_v1 # type: ignore +from google import auth # type: ignore +from google.auth import credentials # type: ignore +from google.auth.transport.grpc import SslCredentials # type: ignore + +import grpc # type: ignore + +from google.devtools.cloudtrace_v2.types import trace +from google.devtools.cloudtrace_v2.types import tracing +from google.protobuf import empty_pb2 as empty # type: ignore + +from .base import TraceServiceTransport, DEFAULT_CLIENT_INFO + + +class TraceServiceGrpcTransport(TraceServiceTransport): + """gRPC backend transport for TraceService. + + This file describes an API for collecting and viewing traces + and spans within a trace. A Trace is a collection of spans + corresponding to a single operation or set of operations for an + application. A span is an individual timed event which forms a + node of the trace tree. A single trace may contain span(s) from + multiple services. + + This class defines the same methods as the primary client, so the + primary client can load the underlying transport implementation + and call it. + + It sends protocol buffers over the wire using gRPC (which is built on + top of HTTP/2); the ``grpcio`` package must be installed. + """ + + _stubs: Dict[str, Callable] + + def __init__( + self, + *, + host: str = "cloudtrace.googleapis.com", + credentials: credentials.Credentials = None, + credentials_file: str = None, + scopes: Sequence[str] = None, + channel: grpc.Channel = None, + api_mtls_endpoint: str = None, + client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, + quota_project_id: Optional[str] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + This argument is ignored if ``channel`` is provided. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional(Sequence[str])): A list of scopes. This argument is + ignored if ``channel`` is provided. + channel (Optional[grpc.Channel]): A ``Channel`` instance through + which to make calls. + api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. + If provided, it overrides the ``host`` argument and tries to create + a mutual TLS channel with client SSL credentials from + ``client_cert_source`` or applicatin default SSL credentials. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): + Deprecated. A callback to provide client SSL certificate bytes and + private key bytes, both in PEM format. It is ignored if + ``api_mtls_endpoint`` is None. + ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials + for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + + Raises: + google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport + creation failed for any reason. + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + self._grpc_channel = None + self._ssl_channel_credentials = ssl_channel_credentials + self._stubs: Dict[str, Callable] = {} + + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + + if channel: + # Ignore credentials if a channel was passed. + credentials = False + # If a channel was explicitly provided, set it. + self._grpc_channel = channel + self._ssl_channel_credentials = None + + else: + if api_mtls_endpoint: + host = api_mtls_endpoint + + # Create SSL credentials with client_cert_source or application + # default SSL credentials. + if client_cert_source: + cert, key = client_cert_source() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + else: + self._ssl_channel_credentials = SslCredentials().ssl_credentials + + else: + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + + # The base transport sets the host, credentials and scopes + super().__init__( + host=host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, + client_info=client_info, + ) + + if not self._grpc_channel: + self._grpc_channel = type(self).create_channel( + self._host, + credentials=self._credentials, + credentials_file=credentials_file, + scopes=self._scopes, + ssl_credentials=self._ssl_channel_credentials, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Wrap messages. This must be done after self._grpc_channel exists + self._prep_wrapped_messages(client_info) + + @classmethod + def create_channel( + cls, + host: str = "cloudtrace.googleapis.com", + credentials: credentials.Credentials = None, + credentials_file: str = None, + scopes: Optional[Sequence[str]] = None, + quota_project_id: Optional[str] = None, + **kwargs, + ) -> grpc.Channel: + """Create and return a gRPC channel object. + Args: + host (Optional[str]): The host for the channel to use. + credentials (Optional[~.Credentials]): The + authorization credentials to attach to requests. These + credentials identify this application to the service. If + none are specified, the client will attempt to ascertain + the credentials from the environment. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is mutually exclusive with credentials. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + kwargs (Optional[dict]): Keyword arguments, which are passed to the + channel creation. + Returns: + grpc.Channel: A gRPC channel object. + + Raises: + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + scopes = scopes or cls.AUTH_SCOPES + return grpc_helpers.create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, + **kwargs, + ) + + @property + def grpc_channel(self) -> grpc.Channel: + """Return the channel designed to connect to this service. + """ + return self._grpc_channel + + @property + def batch_write_spans( + self, + ) -> Callable[[tracing.BatchWriteSpansRequest], empty.Empty]: + r"""Return a callable for the batch write spans method over gRPC. + + Sends new spans to new or existing traces. You cannot + update existing spans. + + Returns: + Callable[[~.BatchWriteSpansRequest], + ~.Empty]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "batch_write_spans" not in self._stubs: + self._stubs["batch_write_spans"] = self.grpc_channel.unary_unary( + "/google.devtools.cloudtrace.v2.TraceService/BatchWriteSpans", + request_serializer=tracing.BatchWriteSpansRequest.serialize, + response_deserializer=empty.Empty.FromString, + ) + return self._stubs["batch_write_spans"] + + @property + def create_span(self) -> Callable[[trace.Span], trace.Span]: + r"""Return a callable for the create span method over gRPC. + + Creates a new span. + + Returns: + Callable[[~.Span], + ~.Span]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "create_span" not in self._stubs: + self._stubs["create_span"] = self.grpc_channel.unary_unary( + "/google.devtools.cloudtrace.v2.TraceService/CreateSpan", + request_serializer=trace.Span.serialize, + response_deserializer=trace.Span.deserialize, + ) + return self._stubs["create_span"] + + +__all__ = ("TraceServiceGrpcTransport",) diff --git a/google/devtools/cloudtrace_v2/services/trace_service/transports/grpc_asyncio.py b/google/devtools/cloudtrace_v2/services/trace_service/transports/grpc_asyncio.py new file mode 100644 index 00000000..68bc1b03 --- /dev/null +++ b/google/devtools/cloudtrace_v2/services/trace_service/transports/grpc_asyncio.py @@ -0,0 +1,287 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 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. +# + +import warnings +from typing import Awaitable, Callable, Dict, Optional, Sequence, Tuple + +from google.api_core import gapic_v1 # type: ignore +from google.api_core import grpc_helpers_async # type: ignore +from google import auth # type: ignore +from google.auth import credentials # type: ignore +from google.auth.transport.grpc import SslCredentials # type: ignore + +import grpc # type: ignore +from grpc.experimental import aio # type: ignore + +from google.devtools.cloudtrace_v2.types import trace +from google.devtools.cloudtrace_v2.types import tracing +from google.protobuf import empty_pb2 as empty # type: ignore + +from .base import TraceServiceTransport, DEFAULT_CLIENT_INFO +from .grpc import TraceServiceGrpcTransport + + +class TraceServiceGrpcAsyncIOTransport(TraceServiceTransport): + """gRPC AsyncIO backend transport for TraceService. + + This file describes an API for collecting and viewing traces + and spans within a trace. A Trace is a collection of spans + corresponding to a single operation or set of operations for an + application. A span is an individual timed event which forms a + node of the trace tree. A single trace may contain span(s) from + multiple services. + + This class defines the same methods as the primary client, so the + primary client can load the underlying transport implementation + and call it. + + It sends protocol buffers over the wire using gRPC (which is built on + top of HTTP/2); the ``grpcio`` package must be installed. + """ + + _grpc_channel: aio.Channel + _stubs: Dict[str, Callable] = {} + + @classmethod + def create_channel( + cls, + host: str = "cloudtrace.googleapis.com", + credentials: credentials.Credentials = None, + credentials_file: Optional[str] = None, + scopes: Optional[Sequence[str]] = None, + quota_project_id: Optional[str] = None, + **kwargs, + ) -> aio.Channel: + """Create and return a gRPC AsyncIO channel object. + Args: + host (Optional[str]): The host for the channel to use. + credentials (Optional[~.Credentials]): The + authorization credentials to attach to requests. These + credentials identify this application to the service. If + none are specified, the client will attempt to ascertain + the credentials from the environment. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + kwargs (Optional[dict]): Keyword arguments, which are passed to the + channel creation. + Returns: + aio.Channel: A gRPC AsyncIO channel object. + """ + scopes = scopes or cls.AUTH_SCOPES + return grpc_helpers_async.create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, + **kwargs, + ) + + def __init__( + self, + *, + host: str = "cloudtrace.googleapis.com", + credentials: credentials.Credentials = None, + credentials_file: Optional[str] = None, + scopes: Optional[Sequence[str]] = None, + channel: aio.Channel = None, + api_mtls_endpoint: str = None, + client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, + quota_project_id=None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + This argument is ignored if ``channel`` is provided. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + channel (Optional[aio.Channel]): A ``Channel`` instance through + which to make calls. + api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. + If provided, it overrides the ``host`` argument and tries to create + a mutual TLS channel with client SSL credentials from + ``client_cert_source`` or applicatin default SSL credentials. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): + Deprecated. A callback to provide client SSL certificate bytes and + private key bytes, both in PEM format. It is ignored if + ``api_mtls_endpoint`` is None. + ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials + for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + + Raises: + google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport + creation failed for any reason. + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + self._grpc_channel = None + self._ssl_channel_credentials = ssl_channel_credentials + self._stubs: Dict[str, Callable] = {} + + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + + if channel: + # Ignore credentials if a channel was passed. + credentials = False + # If a channel was explicitly provided, set it. + self._grpc_channel = channel + self._ssl_channel_credentials = None + + else: + if api_mtls_endpoint: + host = api_mtls_endpoint + + # Create SSL credentials with client_cert_source or application + # default SSL credentials. + if client_cert_source: + cert, key = client_cert_source() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + else: + self._ssl_channel_credentials = SslCredentials().ssl_credentials + + else: + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + + # The base transport sets the host, credentials and scopes + super().__init__( + host=host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, + client_info=client_info, + ) + + if not self._grpc_channel: + self._grpc_channel = type(self).create_channel( + self._host, + credentials=self._credentials, + credentials_file=credentials_file, + scopes=self._scopes, + ssl_credentials=self._ssl_channel_credentials, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Wrap messages. This must be done after self._grpc_channel exists + self._prep_wrapped_messages(client_info) + + @property + def grpc_channel(self) -> aio.Channel: + """Create the channel designed to connect to this service. + + This property caches on the instance; repeated calls return + the same channel. + """ + # Return the channel from cache. + return self._grpc_channel + + @property + def batch_write_spans( + self, + ) -> Callable[[tracing.BatchWriteSpansRequest], Awaitable[empty.Empty]]: + r"""Return a callable for the batch write spans method over gRPC. + + Sends new spans to new or existing traces. You cannot + update existing spans. + + Returns: + Callable[[~.BatchWriteSpansRequest], + Awaitable[~.Empty]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "batch_write_spans" not in self._stubs: + self._stubs["batch_write_spans"] = self.grpc_channel.unary_unary( + "/google.devtools.cloudtrace.v2.TraceService/BatchWriteSpans", + request_serializer=tracing.BatchWriteSpansRequest.serialize, + response_deserializer=empty.Empty.FromString, + ) + return self._stubs["batch_write_spans"] + + @property + def create_span(self) -> Callable[[trace.Span], Awaitable[trace.Span]]: + r"""Return a callable for the create span method over gRPC. + + Creates a new span. + + Returns: + Callable[[~.Span], + Awaitable[~.Span]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "create_span" not in self._stubs: + self._stubs["create_span"] = self.grpc_channel.unary_unary( + "/google.devtools.cloudtrace.v2.TraceService/CreateSpan", + request_serializer=trace.Span.serialize, + response_deserializer=trace.Span.deserialize, + ) + return self._stubs["create_span"] + + +__all__ = ("TraceServiceGrpcAsyncIOTransport",) diff --git a/google/devtools/cloudtrace_v2/types/__init__.py b/google/devtools/cloudtrace_v2/types/__init__.py new file mode 100644 index 00000000..27530d1f --- /dev/null +++ b/google/devtools/cloudtrace_v2/types/__init__.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 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. +# + +from .trace import ( + AttributeValue, + Module, + Span, + StackTrace, + TruncatableString, +) +from .tracing import BatchWriteSpansRequest + +__all__ = ( + "AttributeValue", + "Module", + "Span", + "StackTrace", + "TruncatableString", + "BatchWriteSpansRequest", +) diff --git a/google/devtools/cloudtrace_v2/types/trace.py b/google/devtools/cloudtrace_v2/types/trace.py new file mode 100644 index 00000000..b877dbf8 --- /dev/null +++ b/google/devtools/cloudtrace_v2/types/trace.py @@ -0,0 +1,513 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 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. +# + +import proto # type: ignore + + +from google.protobuf import timestamp_pb2 as timestamp # type: ignore +from google.protobuf import wrappers_pb2 as wrappers # type: ignore +from google.rpc import status_pb2 as gr_status # type: ignore + + +__protobuf__ = proto.module( + package="google.devtools.cloudtrace.v2", + manifest={"Span", "AttributeValue", "StackTrace", "Module", "TruncatableString",}, +) + + +class Span(proto.Message): + r"""A span represents a single operation within a trace. Spans + can be nested to form a trace tree. Often, a trace contains a + root span that describes the end-to-end latency, and one or more + subspans for its sub-operations. A trace can also contain + multiple root spans, or none at all. Spans do not need to be + contiguous—there may be gaps or overlaps between spans in + a trace. + + Attributes: + name (str): + Required. The resource name of the span in the following + format: + + :: + + projects/[PROJECT_ID]/traces/[TRACE_ID]/spans/[SPAN_ID] + + [TRACE_ID] is a unique identifier for a trace within a + project; it is a 32-character hexadecimal encoding of a + 16-byte array. + + [SPAN_ID] is a unique identifier for a span within a trace; + it is a 16-character hexadecimal encoding of an 8-byte + array. + span_id (str): + Required. The [SPAN_ID] portion of the span's resource name. + parent_span_id (str): + The [SPAN_ID] of this span's parent span. If this is a root + span, then this field must be empty. + display_name (google.devtools.cloudtrace_v2.types.TruncatableString): + Required. A description of the span's + operation (up to 128 bytes). Stackdriver Trace + displays the description in the Google Cloud + Platform Console. + For example, the display name can be a qualified + method name or a file name and a line number + where the operation is called. A best practice + is to use the same display name within an + application and at the same call point. This + makes it easier to correlate spans in different + traces. + start_time (google.protobuf.timestamp_pb2.Timestamp): + Required. The start time of the span. On the + client side, this is the time kept by the local + machine where the span execution starts. On the + server side, this is the time when the server's + application handler starts running. + end_time (google.protobuf.timestamp_pb2.Timestamp): + Required. The end time of the span. On the + client side, this is the time kept by the local + machine where the span execution ends. On the + server side, this is the time when the server + application handler stops running. + attributes (google.devtools.cloudtrace_v2.types.Span.Attributes): + A set of attributes on the span. You can have + up to 32 attributes per span. + stack_trace (google.devtools.cloudtrace_v2.types.StackTrace): + Stack trace captured at the start of the + span. + time_events (google.devtools.cloudtrace_v2.types.Span.TimeEvents): + A set of time events. You can have up to 32 + annotations and 128 message events per span. + links (google.devtools.cloudtrace_v2.types.Span.Links): + Links associated with the span. You can have + up to 128 links per Span. + status (google.rpc.status_pb2.Status): + Optional. The final status for this span. + same_process_as_parent_span (google.protobuf.wrappers_pb2.BoolValue): + Optional. Set this parameter to indicate + whether this span is in the same process as its + parent. If you do not set this parameter, + Stackdriver Trace is unable to take advantage of + this helpful information. + child_span_count (google.protobuf.wrappers_pb2.Int32Value): + Optional. The number of child spans that were + generated while this span was active. If set, + allows implementation to detect missing child + spans. + span_kind (google.devtools.cloudtrace_v2.types.Span.SpanKind): + Optional. Distinguishes between spans generated in a + particular context. For example, two spans with the same + name may be distinguished using ``CLIENT`` (caller) and + ``SERVER`` (callee) to identify an RPC call. + """ + + class SpanKind(proto.Enum): + r"""Type of span. Can be used to specify additional relationships + between spans in addition to a parent/child relationship. + """ + SPAN_KIND_UNSPECIFIED = 0 + INTERNAL = 1 + SERVER = 2 + CLIENT = 3 + PRODUCER = 4 + CONSUMER = 5 + + class Attributes(proto.Message): + r"""A set of attributes, each in the format ``[KEY]:[VALUE]``. + + Attributes: + attribute_map (Sequence[google.devtools.cloudtrace_v2.types.Span.Attributes.AttributeMapEntry]): + The set of attributes. Each attribute's key can be up to 128 + bytes long. The value can be a string up to 256 bytes, a + signed 64-bit integer, or the Boolean values ``true`` and + ``false``. For example: + + :: + + "/instance_id": { "string_value": { "value": "my-instance" } } + "/http/request_bytes": { "int_value": 300 } + "abc.com/myattribute": { "bool_value": false } + dropped_attributes_count (int): + The number of attributes that were discarded. + Attributes can be discarded because their keys + are too long or because there are too many + attributes. If this value is 0 then all + attributes are valid. + """ + + attribute_map = proto.MapField( + proto.STRING, proto.MESSAGE, number=1, message="AttributeValue", + ) + + dropped_attributes_count = proto.Field(proto.INT32, number=2) + + class TimeEvent(proto.Message): + r"""A time-stamped annotation or message event in the Span. + + Attributes: + time (google.protobuf.timestamp_pb2.Timestamp): + The timestamp indicating the time the event + occurred. + annotation (google.devtools.cloudtrace_v2.types.Span.TimeEvent.Annotation): + Text annotation with a set of attributes. + message_event (google.devtools.cloudtrace_v2.types.Span.TimeEvent.MessageEvent): + An event describing a message sent/received + between Spans. + """ + + class Annotation(proto.Message): + r"""Text annotation with a set of attributes. + + Attributes: + description (google.devtools.cloudtrace_v2.types.TruncatableString): + A user-supplied message describing the event. + The maximum length for the description is 256 + bytes. + attributes (google.devtools.cloudtrace_v2.types.Span.Attributes): + A set of attributes on the annotation. You + can have up to 4 attributes per Annotation. + """ + + description = proto.Field( + proto.MESSAGE, number=1, message="TruncatableString", + ) + + attributes = proto.Field( + proto.MESSAGE, number=2, message="Span.Attributes", + ) + + class MessageEvent(proto.Message): + r"""An event describing a message sent/received between Spans. + + Attributes: + type_ (google.devtools.cloudtrace_v2.types.Span.TimeEvent.MessageEvent.Type): + Type of MessageEvent. Indicates whether the + message was sent or received. + id (int): + An identifier for the MessageEvent's message + that can be used to match SENT and RECEIVED + MessageEvents. It is recommended to be unique + within a Span. + uncompressed_size_bytes (int): + The number of uncompressed bytes sent or + received. + compressed_size_bytes (int): + The number of compressed bytes sent or + received. If missing assumed to be the same size + as uncompressed. + """ + + class Type(proto.Enum): + r"""Indicates whether the message was sent or received.""" + TYPE_UNSPECIFIED = 0 + SENT = 1 + RECEIVED = 2 + + type_ = proto.Field( + proto.ENUM, number=1, enum="Span.TimeEvent.MessageEvent.Type", + ) + + id = proto.Field(proto.INT64, number=2) + + uncompressed_size_bytes = proto.Field(proto.INT64, number=3) + + compressed_size_bytes = proto.Field(proto.INT64, number=4) + + time = proto.Field(proto.MESSAGE, number=1, message=timestamp.Timestamp,) + + annotation = proto.Field( + proto.MESSAGE, number=2, oneof="value", message="Span.TimeEvent.Annotation", + ) + + message_event = proto.Field( + proto.MESSAGE, + number=3, + oneof="value", + message="Span.TimeEvent.MessageEvent", + ) + + class TimeEvents(proto.Message): + r"""A collection of ``TimeEvent``\ s. A ``TimeEvent`` is a time-stamped + annotation on the span, consisting of either user-supplied key:value + pairs, or details of a message sent/received between Spans. + + Attributes: + time_event (Sequence[google.devtools.cloudtrace_v2.types.Span.TimeEvent]): + A collection of ``TimeEvent``\ s. + dropped_annotations_count (int): + The number of dropped annotations in all the + included time events. If the value is 0, then no + annotations were dropped. + dropped_message_events_count (int): + The number of dropped message events in all + the included time events. If the value is 0, + then no message events were dropped. + """ + + time_event = proto.RepeatedField( + proto.MESSAGE, number=1, message="Span.TimeEvent", + ) + + dropped_annotations_count = proto.Field(proto.INT32, number=2) + + dropped_message_events_count = proto.Field(proto.INT32, number=3) + + class Link(proto.Message): + r"""A pointer from the current span to another span in the same + trace or in a different trace. For example, this can be used in + batching operations, where a single batch handler processes + multiple requests from different traces or when the handler + receives a request from a different project. + + Attributes: + trace_id (str): + The [TRACE_ID] for a trace within a project. + span_id (str): + The [SPAN_ID] for a span within a trace. + type_ (google.devtools.cloudtrace_v2.types.Span.Link.Type): + The relationship of the current span relative + to the linked span. + attributes (google.devtools.cloudtrace_v2.types.Span.Attributes): + A set of attributes on the link. You have + have up to 32 attributes per link. + """ + + class Type(proto.Enum): + r"""The relationship of the current span relative to the linked + span: child, parent, or unspecified. + """ + TYPE_UNSPECIFIED = 0 + CHILD_LINKED_SPAN = 1 + PARENT_LINKED_SPAN = 2 + + trace_id = proto.Field(proto.STRING, number=1) + + span_id = proto.Field(proto.STRING, number=2) + + type_ = proto.Field(proto.ENUM, number=3, enum="Span.Link.Type",) + + attributes = proto.Field(proto.MESSAGE, number=4, message="Span.Attributes",) + + class Links(proto.Message): + r"""A collection of links, which are references from this span to + a span in the same or different trace. + + Attributes: + link (Sequence[google.devtools.cloudtrace_v2.types.Span.Link]): + A collection of links. + dropped_links_count (int): + The number of dropped links after the maximum + size was enforced. If this value is 0, then no + links were dropped. + """ + + link = proto.RepeatedField(proto.MESSAGE, number=1, message="Span.Link",) + + dropped_links_count = proto.Field(proto.INT32, number=2) + + name = proto.Field(proto.STRING, number=1) + + span_id = proto.Field(proto.STRING, number=2) + + parent_span_id = proto.Field(proto.STRING, number=3) + + display_name = proto.Field(proto.MESSAGE, number=4, message="TruncatableString",) + + start_time = proto.Field(proto.MESSAGE, number=5, message=timestamp.Timestamp,) + + end_time = proto.Field(proto.MESSAGE, number=6, message=timestamp.Timestamp,) + + attributes = proto.Field(proto.MESSAGE, number=7, message=Attributes,) + + stack_trace = proto.Field(proto.MESSAGE, number=8, message="StackTrace",) + + time_events = proto.Field(proto.MESSAGE, number=9, message=TimeEvents,) + + links = proto.Field(proto.MESSAGE, number=10, message=Links,) + + status = proto.Field(proto.MESSAGE, number=11, message=gr_status.Status,) + + same_process_as_parent_span = proto.Field( + proto.MESSAGE, number=12, message=wrappers.BoolValue, + ) + + child_span_count = proto.Field( + proto.MESSAGE, number=13, message=wrappers.Int32Value, + ) + + span_kind = proto.Field(proto.ENUM, number=14, enum=SpanKind,) + + +class AttributeValue(proto.Message): + r"""The allowed types for [VALUE] in a ``[KEY]:[VALUE]`` attribute. + + Attributes: + string_value (google.devtools.cloudtrace_v2.types.TruncatableString): + A string up to 256 bytes long. + int_value (int): + A 64-bit signed integer. + bool_value (bool): + A Boolean value represented by ``true`` or ``false``. + """ + + string_value = proto.Field( + proto.MESSAGE, number=1, oneof="value", message="TruncatableString", + ) + + int_value = proto.Field(proto.INT64, number=2, oneof="value") + + bool_value = proto.Field(proto.BOOL, number=3, oneof="value") + + +class StackTrace(proto.Message): + r"""A call stack appearing in a trace. + + Attributes: + stack_frames (google.devtools.cloudtrace_v2.types.StackTrace.StackFrames): + Stack frames in this stack trace. A maximum + of 128 frames are allowed. + stack_trace_hash_id (int): + The hash ID is used to conserve network bandwidth for + duplicate stack traces within a single trace. + + Often multiple spans will have identical stack traces. The + first occurrence of a stack trace should contain both the + ``stackFrame`` content and a value in ``stackTraceHashId``. + + Subsequent spans within the same request can refer to that + stack trace by only setting ``stackTraceHashId``. + """ + + class StackFrame(proto.Message): + r"""Represents a single stack frame in a stack trace. + + Attributes: + function_name (google.devtools.cloudtrace_v2.types.TruncatableString): + The fully-qualified name that uniquely + identifies the function or method that is active + in this frame (up to 1024 bytes). + original_function_name (google.devtools.cloudtrace_v2.types.TruncatableString): + An un-mangled function name, if ``function_name`` is + `mangled `__. + The name can be fully-qualified (up to 1024 bytes). + file_name (google.devtools.cloudtrace_v2.types.TruncatableString): + The name of the source file where the + function call appears (up to 256 bytes). + line_number (int): + The line number in ``file_name`` where the function call + appears. + column_number (int): + The column number where the function call + appears, if available. This is important in + JavaScript because of its anonymous functions. + load_module (google.devtools.cloudtrace_v2.types.Module): + The binary module from where the code was + loaded. + source_version (google.devtools.cloudtrace_v2.types.TruncatableString): + The version of the deployed source code (up + to 128 bytes). + """ + + function_name = proto.Field( + proto.MESSAGE, number=1, message="TruncatableString", + ) + + original_function_name = proto.Field( + proto.MESSAGE, number=2, message="TruncatableString", + ) + + file_name = proto.Field(proto.MESSAGE, number=3, message="TruncatableString",) + + line_number = proto.Field(proto.INT64, number=4) + + column_number = proto.Field(proto.INT64, number=5) + + load_module = proto.Field(proto.MESSAGE, number=6, message="Module",) + + source_version = proto.Field( + proto.MESSAGE, number=7, message="TruncatableString", + ) + + class StackFrames(proto.Message): + r"""A collection of stack frames, which can be truncated. + + Attributes: + frame (Sequence[google.devtools.cloudtrace_v2.types.StackTrace.StackFrame]): + Stack frames in this call stack. + dropped_frames_count (int): + The number of stack frames that were dropped + because there were too many stack frames. + If this value is 0, then no stack frames were + dropped. + """ + + frame = proto.RepeatedField( + proto.MESSAGE, number=1, message="StackTrace.StackFrame", + ) + + dropped_frames_count = proto.Field(proto.INT32, number=2) + + stack_frames = proto.Field(proto.MESSAGE, number=1, message=StackFrames,) + + stack_trace_hash_id = proto.Field(proto.INT64, number=2) + + +class Module(proto.Message): + r"""Binary module. + + Attributes: + module (google.devtools.cloudtrace_v2.types.TruncatableString): + For example: main binary, kernel modules, and + dynamic libraries such as libc.so, sharedlib.so + (up to 256 bytes). + build_id (google.devtools.cloudtrace_v2.types.TruncatableString): + A unique identifier for the module, usually a + hash of its contents (up to 128 bytes). + """ + + module = proto.Field(proto.MESSAGE, number=1, message="TruncatableString",) + + build_id = proto.Field(proto.MESSAGE, number=2, message="TruncatableString",) + + +class TruncatableString(proto.Message): + r"""Represents a string that might be shortened to a specified + length. + + Attributes: + value (str): + The shortened string. For example, if the original string is + 500 bytes long and the limit of the string is 128 bytes, + then ``value`` contains the first 128 bytes of the 500-byte + string. + + Truncation always happens on a UTF8 character boundary. If + there are multi-byte characters in the string, then the + length of the shortened string might be less than the size + limit. + truncated_byte_count (int): + The number of bytes removed from the original + string. If this value is 0, then the string was + not shortened. + """ + + value = proto.Field(proto.STRING, number=1) + + truncated_byte_count = proto.Field(proto.INT32, number=2) + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/devtools/cloudtrace_v2/types/tracing.py b/google/devtools/cloudtrace_v2/types/tracing.py new file mode 100644 index 00000000..9083d55b --- /dev/null +++ b/google/devtools/cloudtrace_v2/types/tracing.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 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. +# + +import proto # type: ignore + + +from google.devtools.cloudtrace_v2.types import trace + + +__protobuf__ = proto.module( + package="google.devtools.cloudtrace.v2", manifest={"BatchWriteSpansRequest",}, +) + + +class BatchWriteSpansRequest(proto.Message): + r"""The request message for the ``BatchWriteSpans`` method. + + Attributes: + name (str): + Required. The name of the project where the spans belong. + The format is ``projects/[PROJECT_ID]``. + spans (Sequence[google.devtools.cloudtrace_v2.types.Span]): + Required. A list of new spans. The span names + must not match existing spans, or the results + are undefined. + """ + + name = proto.Field(proto.STRING, number=1) + + spans = proto.RepeatedField(proto.MESSAGE, number=2, message=trace.Span,) + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/noxfile.py b/noxfile.py index 090ed4b4..b1de6b92 100644 --- a/noxfile.py +++ b/noxfile.py @@ -28,7 +28,7 @@ DEFAULT_PYTHON_VERSION = "3.8" SYSTEM_TEST_PYTHON_VERSIONS = ["3.8"] -UNIT_TEST_PYTHON_VERSIONS = ["3.6", "3.7", "3.8"] +UNIT_TEST_PYTHON_VERSIONS = ["3.6", "3.7", "3.8", "3.9"] @nox.session(python=DEFAULT_PYTHON_VERSION) diff --git a/samples/snippets/noxfile.py b/samples/snippets/noxfile.py index 5660f08b..ba55d7ce 100644 --- a/samples/snippets/noxfile.py +++ b/samples/snippets/noxfile.py @@ -37,22 +37,24 @@ TEST_CONFIG = { # You can opt out from the test for specific Python versions. - "ignored_versions": ["2.7"], + 'ignored_versions': ["2.7"], + # An envvar key for determining the project id to use. Change it # to 'BUILD_SPECIFIC_GCLOUD_PROJECT' if you want to opt in using a # build specific Cloud project. You can also use your own string # to use your own Cloud project. - "gcloud_project_env": "GOOGLE_CLOUD_PROJECT", + 'gcloud_project_env': 'GOOGLE_CLOUD_PROJECT', # 'gcloud_project_env': 'BUILD_SPECIFIC_GCLOUD_PROJECT', + # A dictionary you want to inject into your test. Don't put any # secrets here. These values will override predefined values. - "envs": {}, + 'envs': {}, } try: # Ensure we can import noxfile_config in the project's directory. - sys.path.append(".") + sys.path.append('.') from noxfile_config import TEST_CONFIG_OVERRIDE except ImportError as e: print("No user noxfile_config found: detail: {}".format(e)) @@ -67,12 +69,12 @@ def get_pytest_env_vars(): ret = {} # Override the GCLOUD_PROJECT and the alias. - env_key = TEST_CONFIG["gcloud_project_env"] + env_key = TEST_CONFIG['gcloud_project_env'] # This should error out if not set. - ret["GOOGLE_CLOUD_PROJECT"] = os.environ[env_key] + ret['GOOGLE_CLOUD_PROJECT'] = os.environ[env_key] # Apply user supplied envs. - ret.update(TEST_CONFIG["envs"]) + ret.update(TEST_CONFIG['envs']) return ret @@ -81,7 +83,7 @@ def get_pytest_env_vars(): ALL_VERSIONS = ["2.7", "3.6", "3.7", "3.8"] # Any default versions that should be ignored. -IGNORED_VERSIONS = TEST_CONFIG["ignored_versions"] +IGNORED_VERSIONS = TEST_CONFIG['ignored_versions'] TESTED_VERSIONS = sorted([v for v in ALL_VERSIONS if v not in IGNORED_VERSIONS]) @@ -136,7 +138,7 @@ def lint(session): args = FLAKE8_COMMON_ARGS + [ "--application-import-names", ",".join(local_names), - ".", + "." ] session.run("flake8", *args) @@ -180,9 +182,9 @@ def py(session): if session.python in TESTED_VERSIONS: _session_tests(session) else: - session.skip( - "SKIPPED: {} tests are disabled for this sample.".format(session.python) - ) + session.skip("SKIPPED: {} tests are disabled for this sample.".format( + session.python + )) # diff --git a/scripts/decrypt-secrets.sh b/scripts/decrypt-secrets.sh index 21f6d2a2..ff599eb2 100755 --- a/scripts/decrypt-secrets.sh +++ b/scripts/decrypt-secrets.sh @@ -20,27 +20,14 @@ ROOT=$( dirname "$DIR" ) # Work from the project root. cd $ROOT -# Prevent it from overriding files. -# We recommend that sample authors use their own service account files and cloud project. -# In that case, they are supposed to prepare these files by themselves. -if [[ -f "testing/test-env.sh" ]] || \ - [[ -f "testing/service-account.json" ]] || \ - [[ -f "testing/client-secrets.json" ]]; then - echo "One or more target files exist, aborting." - exit 1 -fi - # Use SECRET_MANAGER_PROJECT if set, fallback to cloud-devrel-kokoro-resources. PROJECT_ID="${SECRET_MANAGER_PROJECT:-cloud-devrel-kokoro-resources}" gcloud secrets versions access latest --secret="python-docs-samples-test-env" \ - --project="${PROJECT_ID}" \ > testing/test-env.sh gcloud secrets versions access latest \ --secret="python-docs-samples-service-account" \ - --project="${PROJECT_ID}" \ > testing/service-account.json gcloud secrets versions access latest \ --secret="python-docs-samples-client-secrets" \ - --project="${PROJECT_ID}" \ - > testing/client-secrets.json + > testing/client-secrets.json \ No newline at end of file diff --git a/scripts/fixup_cloudtrace_v1_keywords.py b/scripts/fixup_cloudtrace_v1_keywords.py new file mode 100644 index 00000000..94ec9308 --- /dev/null +++ b/scripts/fixup_cloudtrace_v1_keywords.py @@ -0,0 +1,181 @@ +#! /usr/bin/env python3 +# -*- coding: utf-8 -*- + +# Copyright 2020 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. +# + +import argparse +import os +import libcst as cst +import pathlib +import sys +from typing import (Any, Callable, Dict, List, Sequence, Tuple) + + +def partition( + predicate: Callable[[Any], bool], + iterator: Sequence[Any] +) -> Tuple[List[Any], List[Any]]: + """A stable, out-of-place partition.""" + results = ([], []) + + for i in iterator: + results[int(predicate(i))].append(i) + + # Returns trueList, falseList + return results[1], results[0] + + +class cloudtraceCallTransformer(cst.CSTTransformer): + CTRL_PARAMS: Tuple[str] = ('retry', 'timeout', 'metadata') + METHOD_TO_PARAMS: Dict[str, Tuple[str]] = { + 'get_trace': ('project_id', 'trace_id', ), + 'list_traces': ('project_id', 'view', 'page_size', 'page_token', 'start_time', 'end_time', 'filter', 'order_by', ), + 'patch_traces': ('project_id', 'traces', ), + + } + + def leave_Call(self, original: cst.Call, updated: cst.Call) -> cst.CSTNode: + try: + key = original.func.attr.value + kword_params = self.METHOD_TO_PARAMS[key] + except (AttributeError, KeyError): + # Either not a method from the API or too convoluted to be sure. + return updated + + # If the existing code is valid, keyword args come after positional args. + # Therefore, all positional args must map to the first parameters. + args, kwargs = partition(lambda a: not bool(a.keyword), updated.args) + if any(k.keyword.value == "request" for k in kwargs): + # We've already fixed this file, don't fix it again. + return updated + + kwargs, ctrl_kwargs = partition( + lambda a: not a.keyword.value in self.CTRL_PARAMS, + kwargs + ) + + args, ctrl_args = args[:len(kword_params)], args[len(kword_params):] + ctrl_kwargs.extend(cst.Arg(value=a.value, keyword=cst.Name(value=ctrl)) + for a, ctrl in zip(ctrl_args, self.CTRL_PARAMS)) + + request_arg = cst.Arg( + value=cst.Dict([ + cst.DictElement( + cst.SimpleString("'{}'".format(name)), + cst.Element(value=arg.value) + ) + # Note: the args + kwargs looks silly, but keep in mind that + # the control parameters had to be stripped out, and that + # those could have been passed positionally or by keyword. + for name, arg in zip(kword_params, args + kwargs)]), + keyword=cst.Name("request") + ) + + return updated.with_changes( + args=[request_arg] + ctrl_kwargs + ) + + +def fix_files( + in_dir: pathlib.Path, + out_dir: pathlib.Path, + *, + transformer=cloudtraceCallTransformer(), +): + """Duplicate the input dir to the output dir, fixing file method calls. + + Preconditions: + * in_dir is a real directory + * out_dir is a real, empty directory + """ + pyfile_gen = ( + pathlib.Path(os.path.join(root, f)) + for root, _, files in os.walk(in_dir) + for f in files if os.path.splitext(f)[1] == ".py" + ) + + for fpath in pyfile_gen: + with open(fpath, 'r') as f: + src = f.read() + + # Parse the code and insert method call fixes. + tree = cst.parse_module(src) + updated = tree.visit(transformer) + + # Create the path and directory structure for the new file. + updated_path = out_dir.joinpath(fpath.relative_to(in_dir)) + updated_path.parent.mkdir(parents=True, exist_ok=True) + + # Generate the updated source file at the corresponding path. + with open(updated_path, 'w') as f: + f.write(updated.code) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser( + description="""Fix up source that uses the cloudtrace client library. + +The existing sources are NOT overwritten but are copied to output_dir with changes made. + +Note: This tool operates at a best-effort level at converting positional + parameters in client method calls to keyword based parameters. + Cases where it WILL FAIL include + A) * or ** expansion in a method call. + B) Calls via function or method alias (includes free function calls) + C) Indirect or dispatched calls (e.g. the method is looked up dynamically) + + These all constitute false negatives. The tool will also detect false + positives when an API method shares a name with another method. +""") + parser.add_argument( + '-d', + '--input-directory', + required=True, + dest='input_dir', + help='the input directory to walk for python files to fix up', + ) + parser.add_argument( + '-o', + '--output-directory', + required=True, + dest='output_dir', + help='the directory to output files fixed via un-flattening', + ) + args = parser.parse_args() + input_dir = pathlib.Path(args.input_dir) + output_dir = pathlib.Path(args.output_dir) + if not input_dir.is_dir(): + print( + f"input directory '{input_dir}' does not exist or is not a directory", + file=sys.stderr, + ) + sys.exit(-1) + + if not output_dir.is_dir(): + print( + f"output directory '{output_dir}' does not exist or is not a directory", + file=sys.stderr, + ) + sys.exit(-1) + + if os.listdir(output_dir): + print( + f"output directory '{output_dir}' is not empty", + file=sys.stderr, + ) + sys.exit(-1) + + fix_files(input_dir, output_dir) diff --git a/scripts/fixup_cloudtrace_v2_keywords.py b/scripts/fixup_cloudtrace_v2_keywords.py new file mode 100644 index 00000000..08772451 --- /dev/null +++ b/scripts/fixup_cloudtrace_v2_keywords.py @@ -0,0 +1,180 @@ +#! /usr/bin/env python3 +# -*- coding: utf-8 -*- + +# Copyright 2020 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. +# + +import argparse +import os +import libcst as cst +import pathlib +import sys +from typing import (Any, Callable, Dict, List, Sequence, Tuple) + + +def partition( + predicate: Callable[[Any], bool], + iterator: Sequence[Any] +) -> Tuple[List[Any], List[Any]]: + """A stable, out-of-place partition.""" + results = ([], []) + + for i in iterator: + results[int(predicate(i))].append(i) + + # Returns trueList, falseList + return results[1], results[0] + + +class cloudtraceCallTransformer(cst.CSTTransformer): + CTRL_PARAMS: Tuple[str] = ('retry', 'timeout', 'metadata') + METHOD_TO_PARAMS: Dict[str, Tuple[str]] = { + 'batch_write_spans': ('name', 'spans', ), + 'create_span': ('name', 'span_id', 'display_name', 'start_time', 'end_time', 'parent_span_id', 'attributes', 'stack_trace', 'time_events', 'links', 'status', 'same_process_as_parent_span', 'child_span_count', 'span_kind', ), + + } + + def leave_Call(self, original: cst.Call, updated: cst.Call) -> cst.CSTNode: + try: + key = original.func.attr.value + kword_params = self.METHOD_TO_PARAMS[key] + except (AttributeError, KeyError): + # Either not a method from the API or too convoluted to be sure. + return updated + + # If the existing code is valid, keyword args come after positional args. + # Therefore, all positional args must map to the first parameters. + args, kwargs = partition(lambda a: not bool(a.keyword), updated.args) + if any(k.keyword.value == "request" for k in kwargs): + # We've already fixed this file, don't fix it again. + return updated + + kwargs, ctrl_kwargs = partition( + lambda a: not a.keyword.value in self.CTRL_PARAMS, + kwargs + ) + + args, ctrl_args = args[:len(kword_params)], args[len(kword_params):] + ctrl_kwargs.extend(cst.Arg(value=a.value, keyword=cst.Name(value=ctrl)) + for a, ctrl in zip(ctrl_args, self.CTRL_PARAMS)) + + request_arg = cst.Arg( + value=cst.Dict([ + cst.DictElement( + cst.SimpleString("'{}'".format(name)), + cst.Element(value=arg.value) + ) + # Note: the args + kwargs looks silly, but keep in mind that + # the control parameters had to be stripped out, and that + # those could have been passed positionally or by keyword. + for name, arg in zip(kword_params, args + kwargs)]), + keyword=cst.Name("request") + ) + + return updated.with_changes( + args=[request_arg] + ctrl_kwargs + ) + + +def fix_files( + in_dir: pathlib.Path, + out_dir: pathlib.Path, + *, + transformer=cloudtraceCallTransformer(), +): + """Duplicate the input dir to the output dir, fixing file method calls. + + Preconditions: + * in_dir is a real directory + * out_dir is a real, empty directory + """ + pyfile_gen = ( + pathlib.Path(os.path.join(root, f)) + for root, _, files in os.walk(in_dir) + for f in files if os.path.splitext(f)[1] == ".py" + ) + + for fpath in pyfile_gen: + with open(fpath, 'r') as f: + src = f.read() + + # Parse the code and insert method call fixes. + tree = cst.parse_module(src) + updated = tree.visit(transformer) + + # Create the path and directory structure for the new file. + updated_path = out_dir.joinpath(fpath.relative_to(in_dir)) + updated_path.parent.mkdir(parents=True, exist_ok=True) + + # Generate the updated source file at the corresponding path. + with open(updated_path, 'w') as f: + f.write(updated.code) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser( + description="""Fix up source that uses the cloudtrace client library. + +The existing sources are NOT overwritten but are copied to output_dir with changes made. + +Note: This tool operates at a best-effort level at converting positional + parameters in client method calls to keyword based parameters. + Cases where it WILL FAIL include + A) * or ** expansion in a method call. + B) Calls via function or method alias (includes free function calls) + C) Indirect or dispatched calls (e.g. the method is looked up dynamically) + + These all constitute false negatives. The tool will also detect false + positives when an API method shares a name with another method. +""") + parser.add_argument( + '-d', + '--input-directory', + required=True, + dest='input_dir', + help='the input directory to walk for python files to fix up', + ) + parser.add_argument( + '-o', + '--output-directory', + required=True, + dest='output_dir', + help='the directory to output files fixed via un-flattening', + ) + args = parser.parse_args() + input_dir = pathlib.Path(args.input_dir) + output_dir = pathlib.Path(args.output_dir) + if not input_dir.is_dir(): + print( + f"input directory '{input_dir}' does not exist or is not a directory", + file=sys.stderr, + ) + sys.exit(-1) + + if not output_dir.is_dir(): + print( + f"output directory '{output_dir}' does not exist or is not a directory", + file=sys.stderr, + ) + sys.exit(-1) + + if os.listdir(output_dir): + print( + f"output directory '{output_dir}' is not empty", + file=sys.stderr, + ) + sys.exit(-1) + + fix_files(input_dir, output_dir) diff --git a/synth.metadata b/synth.metadata index d707a262..75601243 100644 --- a/synth.metadata +++ b/synth.metadata @@ -4,7 +4,15 @@ "git": { "name": ".", "remote": "https://github.com/googleapis/python-trace.git", - "sha": "423fb47f6d3294fca9fe1c942c42d941c3ef3378" + "sha": "2f11aa911b62c439a0a77926c3a6ffd940f065c5" + } + }, + { + "git": { + "name": "googleapis", + "remote": "https://github.com/googleapis/googleapis.git", + "sha": "a1af63efb82f54428ab35ea76869d9cd57ca52b8", + "internalRef": "364635275" } }, { @@ -18,7 +26,7 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "d302f93d7f47e2852e585ac35ab2d15585717ec0" + "sha": "05de3e1e14a0b07eab8b474e669164dbd31f81fb" } } ], @@ -51,7 +59,6 @@ ".github/ISSUE_TEMPLATE/support_request.md", ".github/PULL_REQUEST_TEMPLATE.md", ".github/release-please.yml", - ".github/snippet-bot.yml", ".gitignore", ".kokoro/build.sh", ".kokoro/continuous/common.cfg", @@ -91,40 +98,57 @@ "CONTRIBUTING.rst", "LICENSE", "MANIFEST.in", + "README.rst", "docs/_static/custom.css", "docs/_templates/layout.html", + "docs/cloudtrace_v1/services.rst", + "docs/cloudtrace_v1/trace_service.rst", + "docs/cloudtrace_v1/types.rst", + "docs/cloudtrace_v2/services.rst", + "docs/cloudtrace_v2/trace_service.rst", + "docs/cloudtrace_v2/types.rst", "docs/conf.py", "docs/multiprocessing.rst", - "google/cloud/trace_v1/__init__.py", - "google/cloud/trace_v1/gapic/__init__.py", - "google/cloud/trace_v1/gapic/enums.py", - "google/cloud/trace_v1/gapic/trace_service_client.py", - "google/cloud/trace_v1/gapic/trace_service_client_config.py", - "google/cloud/trace_v1/gapic/transports/__init__.py", - "google/cloud/trace_v1/gapic/transports/trace_service_grpc_transport.py", - "google/cloud/trace_v1/proto/__init__.py", "google/cloud/trace_v1/proto/trace.proto", - "google/cloud/trace_v1/proto/trace_pb2.py", - "google/cloud/trace_v1/proto/trace_pb2_grpc.py", - "google/cloud/trace_v1/types.py", - "google/cloud/trace_v2/__init__.py", - "google/cloud/trace_v2/gapic/__init__.py", - "google/cloud/trace_v2/gapic/enums.py", - "google/cloud/trace_v2/gapic/trace_service_client.py", - "google/cloud/trace_v2/gapic/trace_service_client_config.py", - "google/cloud/trace_v2/gapic/transports/__init__.py", - "google/cloud/trace_v2/gapic/transports/trace_service_grpc_transport.py", - "google/cloud/trace_v2/proto/__init__.py", "google/cloud/trace_v2/proto/trace.proto", - "google/cloud/trace_v2/proto/trace_pb2.py", - "google/cloud/trace_v2/proto/trace_pb2_grpc.py", "google/cloud/trace_v2/proto/tracing.proto", - "google/cloud/trace_v2/proto/tracing_pb2.py", - "google/cloud/trace_v2/proto/tracing_pb2_grpc.py", - "google/cloud/trace_v2/types.py", + "google/devtools/cloudtrace/__init__.py", + "google/devtools/cloudtrace/py.typed", + "google/devtools/cloudtrace_v1/__init__.py", + "google/devtools/cloudtrace_v1/py.typed", + "google/devtools/cloudtrace_v1/services/__init__.py", + "google/devtools/cloudtrace_v1/services/trace_service/__init__.py", + "google/devtools/cloudtrace_v1/services/trace_service/async_client.py", + "google/devtools/cloudtrace_v1/services/trace_service/client.py", + "google/devtools/cloudtrace_v1/services/trace_service/pagers.py", + "google/devtools/cloudtrace_v1/services/trace_service/transports/__init__.py", + "google/devtools/cloudtrace_v1/services/trace_service/transports/base.py", + "google/devtools/cloudtrace_v1/services/trace_service/transports/grpc.py", + "google/devtools/cloudtrace_v1/services/trace_service/transports/grpc_asyncio.py", + "google/devtools/cloudtrace_v1/types/__init__.py", + "google/devtools/cloudtrace_v1/types/trace.py", + "google/devtools/cloudtrace_v2/__init__.py", + "google/devtools/cloudtrace_v2/py.typed", + "google/devtools/cloudtrace_v2/services/__init__.py", + "google/devtools/cloudtrace_v2/services/trace_service/__init__.py", + "google/devtools/cloudtrace_v2/services/trace_service/async_client.py", + "google/devtools/cloudtrace_v2/services/trace_service/client.py", + "google/devtools/cloudtrace_v2/services/trace_service/transports/__init__.py", + "google/devtools/cloudtrace_v2/services/trace_service/transports/base.py", + "google/devtools/cloudtrace_v2/services/trace_service/transports/grpc.py", + "google/devtools/cloudtrace_v2/services/trace_service/transports/grpc_asyncio.py", + "google/devtools/cloudtrace_v2/types/__init__.py", + "google/devtools/cloudtrace_v2/types/trace.py", + "google/devtools/cloudtrace_v2/types/tracing.py", + "mypy.ini", "noxfile.py", "renovate.json", + "samples/AUTHORING_GUIDE.md", + "samples/CONTRIBUTING.md", + "samples/snippets/noxfile.py", "scripts/decrypt-secrets.sh", + "scripts/fixup_cloudtrace_v1_keywords.py", + "scripts/fixup_cloudtrace_v2_keywords.py", "scripts/readme-gen/readme_gen.py", "scripts/readme-gen/templates/README.tmpl.rst", "scripts/readme-gen/templates/auth.tmpl.rst", @@ -133,7 +157,9 @@ "scripts/readme-gen/templates/install_portaudio.tmpl.rst", "setup.cfg", "testing/.gitignore", - "tests/unit/gapic/v1/test_trace_service_client_v1.py", - "tests/unit/gapic/v2/test_trace_service_client_v2.py" + "tests/unit/gapic/cloudtrace_v1/__init__.py", + "tests/unit/gapic/cloudtrace_v1/test_trace_service.py", + "tests/unit/gapic/cloudtrace_v2/__init__.py", + "tests/unit/gapic/cloudtrace_v2/test_trace_service.py" ] } \ No newline at end of file diff --git a/tests/unit/gapic/cloudtrace_v1/__init__.py b/tests/unit/gapic/cloudtrace_v1/__init__.py new file mode 100644 index 00000000..42ffdf2b --- /dev/null +++ b/tests/unit/gapic/cloudtrace_v1/__init__.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 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. +# diff --git a/tests/unit/gapic/cloudtrace_v1/test_trace_service.py b/tests/unit/gapic/cloudtrace_v1/test_trace_service.py new file mode 100644 index 00000000..1cd92988 --- /dev/null +++ b/tests/unit/gapic/cloudtrace_v1/test_trace_service.py @@ -0,0 +1,1507 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 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. +# + +import os +import mock + +import grpc +from grpc.experimental import aio +import math +import pytest +from proto.marshal.rules.dates import DurationRule, TimestampRule + +from google import auth +from google.api_core import client_options +from google.api_core import exceptions +from google.api_core import gapic_v1 +from google.api_core import grpc_helpers +from google.api_core import grpc_helpers_async +from google.auth import credentials +from google.auth.exceptions import MutualTLSChannelError +from google.devtools.cloudtrace_v1.services.trace_service import TraceServiceAsyncClient +from google.devtools.cloudtrace_v1.services.trace_service import TraceServiceClient +from google.devtools.cloudtrace_v1.services.trace_service import pagers +from google.devtools.cloudtrace_v1.services.trace_service import transports +from google.devtools.cloudtrace_v1.types import trace +from google.oauth2 import service_account +from google.protobuf import timestamp_pb2 as timestamp # type: ignore + + +def client_cert_source_callback(): + return b"cert bytes", b"key bytes" + + +# If default endpoint is localhost, then default mtls endpoint will be the same. +# This method modifies the default endpoint so the client can produce a different +# mtls endpoint for endpoint testing purposes. +def modify_default_endpoint(client): + return ( + "foo.googleapis.com" + if ("localhost" in client.DEFAULT_ENDPOINT) + else client.DEFAULT_ENDPOINT + ) + + +def test__get_default_mtls_endpoint(): + api_endpoint = "example.googleapis.com" + api_mtls_endpoint = "example.mtls.googleapis.com" + sandbox_endpoint = "example.sandbox.googleapis.com" + sandbox_mtls_endpoint = "example.mtls.sandbox.googleapis.com" + non_googleapi = "api.example.com" + + assert TraceServiceClient._get_default_mtls_endpoint(None) is None + assert ( + TraceServiceClient._get_default_mtls_endpoint(api_endpoint) == api_mtls_endpoint + ) + assert ( + TraceServiceClient._get_default_mtls_endpoint(api_mtls_endpoint) + == api_mtls_endpoint + ) + assert ( + TraceServiceClient._get_default_mtls_endpoint(sandbox_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + TraceServiceClient._get_default_mtls_endpoint(sandbox_mtls_endpoint) + == sandbox_mtls_endpoint + ) + assert TraceServiceClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi + + +@pytest.mark.parametrize("client_class", [TraceServiceClient, TraceServiceAsyncClient,]) +def test_trace_service_client_from_service_account_info(client_class): + creds = credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_info" + ) as factory: + factory.return_value = creds + info = {"valid": True} + client = client_class.from_service_account_info(info) + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "cloudtrace.googleapis.com:443" + + +@pytest.mark.parametrize("client_class", [TraceServiceClient, TraceServiceAsyncClient,]) +def test_trace_service_client_from_service_account_file(client_class): + creds = credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_file" + ) as factory: + factory.return_value = creds + client = client_class.from_service_account_file("dummy/file/path.json") + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + client = client_class.from_service_account_json("dummy/file/path.json") + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "cloudtrace.googleapis.com:443" + + +def test_trace_service_client_get_transport_class(): + transport = TraceServiceClient.get_transport_class() + available_transports = [ + transports.TraceServiceGrpcTransport, + ] + assert transport in available_transports + + transport = TraceServiceClient.get_transport_class("grpc") + assert transport == transports.TraceServiceGrpcTransport + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (TraceServiceClient, transports.TraceServiceGrpcTransport, "grpc"), + ( + TraceServiceAsyncClient, + transports.TraceServiceGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +@mock.patch.object( + TraceServiceClient, "DEFAULT_ENDPOINT", modify_default_endpoint(TraceServiceClient) +) +@mock.patch.object( + TraceServiceAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(TraceServiceAsyncClient), +) +def test_trace_service_client_client_options( + client_class, transport_class, transport_name +): + # Check that if channel is provided we won't create a new one. + with mock.patch.object(TraceServiceClient, "get_transport_class") as gtc: + transport = transport_class(credentials=credentials.AnonymousCredentials()) + client = client_class(transport=transport) + gtc.assert_not_called() + + # Check that if channel is provided via str we will create a new one. + with mock.patch.object(TraceServiceClient, "get_transport_class") as gtc: + client = client_class(transport=transport_name) + gtc.assert_called() + + # Check the case api_endpoint is provided. + options = client_options.ClientOptions(api_endpoint="squid.clam.whelk") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is + # "never". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}): + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is + # "always". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}): + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_MTLS_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT has + # unsupported value. + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}): + with pytest.raises(MutualTLSChannelError): + client = client_class() + + # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} + ): + with pytest.raises(ValueError): + client = client_class() + + # Check the case quota_project_id is provided + options = client_options.ClientOptions(quota_project_id="octopus") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id="octopus", + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name,use_client_cert_env", + [ + (TraceServiceClient, transports.TraceServiceGrpcTransport, "grpc", "true"), + ( + TraceServiceAsyncClient, + transports.TraceServiceGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + (TraceServiceClient, transports.TraceServiceGrpcTransport, "grpc", "false"), + ( + TraceServiceAsyncClient, + transports.TraceServiceGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + TraceServiceClient, "DEFAULT_ENDPOINT", modify_default_endpoint(TraceServiceClient) +) +@mock.patch.object( + TraceServiceAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(TraceServiceAsyncClient), +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_trace_service_client_mtls_env_auto( + client_class, transport_class, transport_name, use_client_cert_env +): + # This tests the endpoint autoswitch behavior. Endpoint is autoswitched to the default + # mtls endpoint, if GOOGLE_API_USE_CLIENT_CERTIFICATE is "true" and client cert exists. + + # Check the case client_cert_source is provided. Whether client cert is used depends on + # GOOGLE_API_USE_CLIENT_CERTIFICATE value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + options = client_options.ClientOptions( + client_cert_source=client_cert_source_callback + ) + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + + if use_client_cert_env == "false": + expected_client_cert_source = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_client_cert_source = client_cert_source_callback + expected_host = client.DEFAULT_MTLS_ENDPOINT + + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case ADC client cert is provided. Whether client cert is used depends on + # GOOGLE_API_USE_CLIENT_CERTIFICATE value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=True, + ): + with mock.patch( + "google.auth.transport.mtls.default_client_cert_source", + return_value=client_cert_source_callback, + ): + if use_client_cert_env == "false": + expected_host = client.DEFAULT_ENDPOINT + expected_client_cert_source = None + else: + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_client_cert_source = client_cert_source_callback + + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=False, + ): + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (TraceServiceClient, transports.TraceServiceGrpcTransport, "grpc"), + ( + TraceServiceAsyncClient, + transports.TraceServiceGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_trace_service_client_client_options_scopes( + client_class, transport_class, transport_name +): + # Check the case scopes are provided. + options = client_options.ClientOptions(scopes=["1", "2"],) + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=["1", "2"], + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (TraceServiceClient, transports.TraceServiceGrpcTransport, "grpc"), + ( + TraceServiceAsyncClient, + transports.TraceServiceGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_trace_service_client_client_options_credentials_file( + client_class, transport_class, transport_name +): + # Check the case credentials file is provided. + options = client_options.ClientOptions(credentials_file="credentials.json") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file="credentials.json", + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_trace_service_client_client_options_from_dict(): + with mock.patch( + "google.devtools.cloudtrace_v1.services.trace_service.transports.TraceServiceGrpcTransport.__init__" + ) as grpc_transport: + grpc_transport.return_value = None + client = TraceServiceClient(client_options={"api_endpoint": "squid.clam.whelk"}) + grpc_transport.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_list_traces(transport: str = "grpc", request_type=trace.ListTracesRequest): + client = TraceServiceClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_traces), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = trace.ListTracesResponse( + next_page_token="next_page_token_value", + ) + + response = client.list_traces(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == trace.ListTracesRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, pagers.ListTracesPager) + + assert response.next_page_token == "next_page_token_value" + + +def test_list_traces_from_dict(): + test_list_traces(request_type=dict) + + +def test_list_traces_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = TraceServiceClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_traces), "__call__") as call: + client.list_traces() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == trace.ListTracesRequest() + + +@pytest.mark.asyncio +async def test_list_traces_async( + transport: str = "grpc_asyncio", request_type=trace.ListTracesRequest +): + client = TraceServiceAsyncClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_traces), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + trace.ListTracesResponse(next_page_token="next_page_token_value",) + ) + + response = await client.list_traces(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == trace.ListTracesRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListTracesAsyncPager) + + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.asyncio +async def test_list_traces_async_from_dict(): + await test_list_traces_async(request_type=dict) + + +def test_list_traces_flattened(): + client = TraceServiceClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_traces), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = trace.ListTracesResponse() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.list_traces(project_id="project_id_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0].project_id == "project_id_value" + + +def test_list_traces_flattened_error(): + client = TraceServiceClient(credentials=credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.list_traces( + trace.ListTracesRequest(), project_id="project_id_value", + ) + + +@pytest.mark.asyncio +async def test_list_traces_flattened_async(): + client = TraceServiceAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_traces), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = trace.ListTracesResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + trace.ListTracesResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.list_traces(project_id="project_id_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0].project_id == "project_id_value" + + +@pytest.mark.asyncio +async def test_list_traces_flattened_error_async(): + client = TraceServiceAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.list_traces( + trace.ListTracesRequest(), project_id="project_id_value", + ) + + +def test_list_traces_pager(): + client = TraceServiceClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_traces), "__call__") as call: + # Set the response to a series of pages. + call.side_effect = ( + trace.ListTracesResponse( + traces=[trace.Trace(), trace.Trace(), trace.Trace(),], + next_page_token="abc", + ), + trace.ListTracesResponse(traces=[], next_page_token="def",), + trace.ListTracesResponse(traces=[trace.Trace(),], next_page_token="ghi",), + trace.ListTracesResponse(traces=[trace.Trace(), trace.Trace(),],), + RuntimeError, + ) + + metadata = () + pager = client.list_traces(request={}) + + assert pager._metadata == metadata + + results = [i for i in pager] + assert len(results) == 6 + assert all(isinstance(i, trace.Trace) for i in results) + + +def test_list_traces_pages(): + client = TraceServiceClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_traces), "__call__") as call: + # Set the response to a series of pages. + call.side_effect = ( + trace.ListTracesResponse( + traces=[trace.Trace(), trace.Trace(), trace.Trace(),], + next_page_token="abc", + ), + trace.ListTracesResponse(traces=[], next_page_token="def",), + trace.ListTracesResponse(traces=[trace.Trace(),], next_page_token="ghi",), + trace.ListTracesResponse(traces=[trace.Trace(), trace.Trace(),],), + RuntimeError, + ) + pages = list(client.list_traces(request={}).pages) + for page_, token in zip(pages, ["abc", "def", "ghi", ""]): + assert page_.raw_page.next_page_token == token + + +@pytest.mark.asyncio +async def test_list_traces_async_pager(): + client = TraceServiceAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_traces), "__call__", new_callable=mock.AsyncMock + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + trace.ListTracesResponse( + traces=[trace.Trace(), trace.Trace(), trace.Trace(),], + next_page_token="abc", + ), + trace.ListTracesResponse(traces=[], next_page_token="def",), + trace.ListTracesResponse(traces=[trace.Trace(),], next_page_token="ghi",), + trace.ListTracesResponse(traces=[trace.Trace(), trace.Trace(),],), + RuntimeError, + ) + async_pager = await client.list_traces(request={},) + assert async_pager.next_page_token == "abc" + responses = [] + async for response in async_pager: + responses.append(response) + + assert len(responses) == 6 + assert all(isinstance(i, trace.Trace) for i in responses) + + +@pytest.mark.asyncio +async def test_list_traces_async_pages(): + client = TraceServiceAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_traces), "__call__", new_callable=mock.AsyncMock + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + trace.ListTracesResponse( + traces=[trace.Trace(), trace.Trace(), trace.Trace(),], + next_page_token="abc", + ), + trace.ListTracesResponse(traces=[], next_page_token="def",), + trace.ListTracesResponse(traces=[trace.Trace(),], next_page_token="ghi",), + trace.ListTracesResponse(traces=[trace.Trace(), trace.Trace(),],), + RuntimeError, + ) + pages = [] + async for page_ in (await client.list_traces(request={})).pages: + pages.append(page_) + for page_, token in zip(pages, ["abc", "def", "ghi", ""]): + assert page_.raw_page.next_page_token == token + + +def test_get_trace(transport: str = "grpc", request_type=trace.GetTraceRequest): + client = TraceServiceClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_trace), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = trace.Trace( + project_id="project_id_value", trace_id="trace_id_value", + ) + + response = client.get_trace(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == trace.GetTraceRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, trace.Trace) + + assert response.project_id == "project_id_value" + + assert response.trace_id == "trace_id_value" + + +def test_get_trace_from_dict(): + test_get_trace(request_type=dict) + + +def test_get_trace_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = TraceServiceClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_trace), "__call__") as call: + client.get_trace() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == trace.GetTraceRequest() + + +@pytest.mark.asyncio +async def test_get_trace_async( + transport: str = "grpc_asyncio", request_type=trace.GetTraceRequest +): + client = TraceServiceAsyncClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_trace), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + trace.Trace(project_id="project_id_value", trace_id="trace_id_value",) + ) + + response = await client.get_trace(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == trace.GetTraceRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, trace.Trace) + + assert response.project_id == "project_id_value" + + assert response.trace_id == "trace_id_value" + + +@pytest.mark.asyncio +async def test_get_trace_async_from_dict(): + await test_get_trace_async(request_type=dict) + + +def test_get_trace_flattened(): + client = TraceServiceClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_trace), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = trace.Trace() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.get_trace( + project_id="project_id_value", trace_id="trace_id_value", + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0].project_id == "project_id_value" + + assert args[0].trace_id == "trace_id_value" + + +def test_get_trace_flattened_error(): + client = TraceServiceClient(credentials=credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.get_trace( + trace.GetTraceRequest(), + project_id="project_id_value", + trace_id="trace_id_value", + ) + + +@pytest.mark.asyncio +async def test_get_trace_flattened_async(): + client = TraceServiceAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_trace), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = trace.Trace() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(trace.Trace()) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.get_trace( + project_id="project_id_value", trace_id="trace_id_value", + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0].project_id == "project_id_value" + + assert args[0].trace_id == "trace_id_value" + + +@pytest.mark.asyncio +async def test_get_trace_flattened_error_async(): + client = TraceServiceAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.get_trace( + trace.GetTraceRequest(), + project_id="project_id_value", + trace_id="trace_id_value", + ) + + +def test_patch_traces(transport: str = "grpc", request_type=trace.PatchTracesRequest): + client = TraceServiceClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.patch_traces), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = None + + response = client.patch_traces(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == trace.PatchTracesRequest() + + # Establish that the response is the type that we expect. + assert response is None + + +def test_patch_traces_from_dict(): + test_patch_traces(request_type=dict) + + +def test_patch_traces_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = TraceServiceClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.patch_traces), "__call__") as call: + client.patch_traces() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == trace.PatchTracesRequest() + + +@pytest.mark.asyncio +async def test_patch_traces_async( + transport: str = "grpc_asyncio", request_type=trace.PatchTracesRequest +): + client = TraceServiceAsyncClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.patch_traces), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + + response = await client.patch_traces(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == trace.PatchTracesRequest() + + # Establish that the response is the type that we expect. + assert response is None + + +@pytest.mark.asyncio +async def test_patch_traces_async_from_dict(): + await test_patch_traces_async(request_type=dict) + + +def test_patch_traces_flattened(): + client = TraceServiceClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.patch_traces), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = None + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.patch_traces( + project_id="project_id_value", + traces=trace.Traces(traces=[trace.Trace(project_id="project_id_value")]), + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0].project_id == "project_id_value" + + assert args[0].traces == trace.Traces( + traces=[trace.Trace(project_id="project_id_value")] + ) + + +def test_patch_traces_flattened_error(): + client = TraceServiceClient(credentials=credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.patch_traces( + trace.PatchTracesRequest(), + project_id="project_id_value", + traces=trace.Traces(traces=[trace.Trace(project_id="project_id_value")]), + ) + + +@pytest.mark.asyncio +async def test_patch_traces_flattened_async(): + client = TraceServiceAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.patch_traces), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = None + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.patch_traces( + project_id="project_id_value", + traces=trace.Traces(traces=[trace.Trace(project_id="project_id_value")]), + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0].project_id == "project_id_value" + + assert args[0].traces == trace.Traces( + traces=[trace.Trace(project_id="project_id_value")] + ) + + +@pytest.mark.asyncio +async def test_patch_traces_flattened_error_async(): + client = TraceServiceAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.patch_traces( + trace.PatchTracesRequest(), + project_id="project_id_value", + traces=trace.Traces(traces=[trace.Trace(project_id="project_id_value")]), + ) + + +def test_credentials_transport_error(): + # It is an error to provide credentials and a transport instance. + transport = transports.TraceServiceGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = TraceServiceClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # It is an error to provide a credentials file and a transport instance. + transport = transports.TraceServiceGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = TraceServiceClient( + client_options={"credentials_file": "credentials.json"}, + transport=transport, + ) + + # It is an error to provide scopes and a transport instance. + transport = transports.TraceServiceGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = TraceServiceClient( + client_options={"scopes": ["1", "2"]}, transport=transport, + ) + + +def test_transport_instance(): + # A client may be instantiated with a custom transport instance. + transport = transports.TraceServiceGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + client = TraceServiceClient(transport=transport) + assert client.transport is transport + + +def test_transport_get_channel(): + # A client may be instantiated with a custom transport instance. + transport = transports.TraceServiceGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + transport = transports.TraceServiceGrpcAsyncIOTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + +@pytest.mark.parametrize( + "transport_class", + [ + transports.TraceServiceGrpcTransport, + transports.TraceServiceGrpcAsyncIOTransport, + ], +) +def test_transport_adc(transport_class): + # Test default credentials are used if not provided. + with mock.patch.object(auth, "default") as adc: + adc.return_value = (credentials.AnonymousCredentials(), None) + transport_class() + adc.assert_called_once() + + +def test_transport_grpc_default(): + # A client should use the gRPC transport by default. + client = TraceServiceClient(credentials=credentials.AnonymousCredentials(),) + assert isinstance(client.transport, transports.TraceServiceGrpcTransport,) + + +def test_trace_service_base_transport_error(): + # Passing both a credentials object and credentials_file should raise an error + with pytest.raises(exceptions.DuplicateCredentialArgs): + transport = transports.TraceServiceTransport( + credentials=credentials.AnonymousCredentials(), + credentials_file="credentials.json", + ) + + +def test_trace_service_base_transport(): + # Instantiate the base transport. + with mock.patch( + "google.devtools.cloudtrace_v1.services.trace_service.transports.TraceServiceTransport.__init__" + ) as Transport: + Transport.return_value = None + transport = transports.TraceServiceTransport( + credentials=credentials.AnonymousCredentials(), + ) + + # Every method on the transport should just blindly + # raise NotImplementedError. + methods = ( + "list_traces", + "get_trace", + "patch_traces", + ) + for method in methods: + with pytest.raises(NotImplementedError): + getattr(transport, method)(request=object()) + + +def test_trace_service_base_transport_with_credentials_file(): + # Instantiate the base transport with a credentials file + with mock.patch.object( + auth, "load_credentials_from_file" + ) as load_creds, mock.patch( + "google.devtools.cloudtrace_v1.services.trace_service.transports.TraceServiceTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + load_creds.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.TraceServiceTransport( + credentials_file="credentials.json", quota_project_id="octopus", + ) + load_creds.assert_called_once_with( + "credentials.json", + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/trace.append", + "https://www.googleapis.com/auth/trace.readonly", + ), + quota_project_id="octopus", + ) + + +def test_trace_service_base_transport_with_adc(): + # Test the default credentials are used if credentials and credentials_file are None. + with mock.patch.object(auth, "default") as adc, mock.patch( + "google.devtools.cloudtrace_v1.services.trace_service.transports.TraceServiceTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.TraceServiceTransport() + adc.assert_called_once() + + +def test_trace_service_auth_adc(): + # If no credentials are provided, we should use ADC credentials. + with mock.patch.object(auth, "default") as adc: + adc.return_value = (credentials.AnonymousCredentials(), None) + TraceServiceClient() + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/trace.append", + "https://www.googleapis.com/auth/trace.readonly", + ), + quota_project_id=None, + ) + + +def test_trace_service_transport_auth_adc(): + # If credentials and host are not provided, the transport class should use + # ADC credentials. + with mock.patch.object(auth, "default") as adc: + adc.return_value = (credentials.AnonymousCredentials(), None) + transports.TraceServiceGrpcTransport( + host="squid.clam.whelk", quota_project_id="octopus" + ) + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/trace.append", + "https://www.googleapis.com/auth/trace.readonly", + ), + quota_project_id="octopus", + ) + + +@pytest.mark.parametrize( + "transport_class", + [transports.TraceServiceGrpcTransport, transports.TraceServiceGrpcAsyncIOTransport], +) +def test_trace_service_grpc_transport_client_cert_source_for_mtls(transport_class): + cred = credentials.AnonymousCredentials() + + # Check ssl_channel_credentials is used if provided. + with mock.patch.object(transport_class, "create_channel") as mock_create_channel: + mock_ssl_channel_creds = mock.Mock() + transport_class( + host="squid.clam.whelk", + credentials=cred, + ssl_channel_credentials=mock_ssl_channel_creds, + ) + mock_create_channel.assert_called_once_with( + "squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/trace.append", + "https://www.googleapis.com/auth/trace.readonly", + ), + ssl_credentials=mock_ssl_channel_creds, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Check if ssl_channel_credentials is not provided, then client_cert_source_for_mtls + # is used. + with mock.patch.object(transport_class, "create_channel", return_value=mock.Mock()): + with mock.patch("grpc.ssl_channel_credentials") as mock_ssl_cred: + transport_class( + credentials=cred, + client_cert_source_for_mtls=client_cert_source_callback, + ) + expected_cert, expected_key = client_cert_source_callback() + mock_ssl_cred.assert_called_once_with( + certificate_chain=expected_cert, private_key=expected_key + ) + + +def test_trace_service_host_no_port(): + client = TraceServiceClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="cloudtrace.googleapis.com" + ), + ) + assert client.transport._host == "cloudtrace.googleapis.com:443" + + +def test_trace_service_host_with_port(): + client = TraceServiceClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="cloudtrace.googleapis.com:8000" + ), + ) + assert client.transport._host == "cloudtrace.googleapis.com:8000" + + +def test_trace_service_grpc_transport_channel(): + channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) + + # Check that channel is used if provided. + transport = transports.TraceServiceGrpcTransport( + host="squid.clam.whelk", channel=channel, + ) + assert transport.grpc_channel == channel + assert transport._host == "squid.clam.whelk:443" + assert transport._ssl_channel_credentials == None + + +def test_trace_service_grpc_asyncio_transport_channel(): + channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) + + # Check that channel is used if provided. + transport = transports.TraceServiceGrpcAsyncIOTransport( + host="squid.clam.whelk", channel=channel, + ) + assert transport.grpc_channel == channel + assert transport._host == "squid.clam.whelk:443" + assert transport._ssl_channel_credentials == None + + +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. +@pytest.mark.parametrize( + "transport_class", + [transports.TraceServiceGrpcTransport, transports.TraceServiceGrpcAsyncIOTransport], +) +def test_trace_service_transport_channel_mtls_with_client_cert_source(transport_class): + with mock.patch( + "grpc.ssl_channel_credentials", autospec=True + ) as grpc_ssl_channel_cred: + with mock.patch.object( + transport_class, "create_channel" + ) as grpc_create_channel: + mock_ssl_cred = mock.Mock() + grpc_ssl_channel_cred.return_value = mock_ssl_cred + + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + + cred = credentials.AnonymousCredentials() + with pytest.warns(DeprecationWarning): + with mock.patch.object(auth, "default") as adc: + adc.return_value = (cred, None) + transport = transport_class( + host="squid.clam.whelk", + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=client_cert_source_callback, + ) + adc.assert_called_once() + + grpc_ssl_channel_cred.assert_called_once_with( + certificate_chain=b"cert bytes", private_key=b"key bytes" + ) + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/trace.append", + "https://www.googleapis.com/auth/trace.readonly", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred + + +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. +@pytest.mark.parametrize( + "transport_class", + [transports.TraceServiceGrpcTransport, transports.TraceServiceGrpcAsyncIOTransport], +) +def test_trace_service_transport_channel_mtls_with_adc(transport_class): + mock_ssl_cred = mock.Mock() + with mock.patch.multiple( + "google.auth.transport.grpc.SslCredentials", + __init__=mock.Mock(return_value=None), + ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), + ): + with mock.patch.object( + transport_class, "create_channel" + ) as grpc_create_channel: + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + mock_cred = mock.Mock() + + with pytest.warns(DeprecationWarning): + transport = transport_class( + host="squid.clam.whelk", + credentials=mock_cred, + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=None, + ) + + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=mock_cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/trace.append", + "https://www.googleapis.com/auth/trace.readonly", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + assert transport.grpc_channel == mock_grpc_channel + + +def test_common_billing_account_path(): + billing_account = "squid" + + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = TraceServiceClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "clam", + } + path = TraceServiceClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = TraceServiceClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "whelk" + + expected = "folders/{folder}".format(folder=folder,) + actual = TraceServiceClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "octopus", + } + path = TraceServiceClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = TraceServiceClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "oyster" + + expected = "organizations/{organization}".format(organization=organization,) + actual = TraceServiceClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "nudibranch", + } + path = TraceServiceClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = TraceServiceClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "cuttlefish" + + expected = "projects/{project}".format(project=project,) + actual = TraceServiceClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "mussel", + } + path = TraceServiceClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = TraceServiceClient.parse_common_project_path(path) + assert expected == actual + + +def test_common_location_path(): + project = "winkle" + location = "nautilus" + + expected = "projects/{project}/locations/{location}".format( + project=project, location=location, + ) + actual = TraceServiceClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "scallop", + "location": "abalone", + } + path = TraceServiceClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = TraceServiceClient.parse_common_location_path(path) + assert expected == actual + + +def test_client_withDEFAULT_CLIENT_INFO(): + client_info = gapic_v1.client_info.ClientInfo() + + with mock.patch.object( + transports.TraceServiceTransport, "_prep_wrapped_messages" + ) as prep: + client = TraceServiceClient( + credentials=credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) + + with mock.patch.object( + transports.TraceServiceTransport, "_prep_wrapped_messages" + ) as prep: + transport_class = TraceServiceClient.get_transport_class() + transport = transport_class( + credentials=credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) diff --git a/tests/unit/gapic/cloudtrace_v2/__init__.py b/tests/unit/gapic/cloudtrace_v2/__init__.py new file mode 100644 index 00000000..42ffdf2b --- /dev/null +++ b/tests/unit/gapic/cloudtrace_v2/__init__.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 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. +# diff --git a/tests/unit/gapic/cloudtrace_v2/test_trace_service.py b/tests/unit/gapic/cloudtrace_v2/test_trace_service.py new file mode 100644 index 00000000..16570b99 --- /dev/null +++ b/tests/unit/gapic/cloudtrace_v2/test_trace_service.py @@ -0,0 +1,1316 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 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. +# + +import os +import mock + +import grpc +from grpc.experimental import aio +import math +import pytest +from proto.marshal.rules.dates import DurationRule, TimestampRule + +from google import auth +from google.api_core import client_options +from google.api_core import exceptions +from google.api_core import gapic_v1 +from google.api_core import grpc_helpers +from google.api_core import grpc_helpers_async +from google.auth import credentials +from google.auth.exceptions import MutualTLSChannelError +from google.devtools.cloudtrace_v2.services.trace_service import TraceServiceAsyncClient +from google.devtools.cloudtrace_v2.services.trace_service import TraceServiceClient +from google.devtools.cloudtrace_v2.services.trace_service import transports +from google.devtools.cloudtrace_v2.types import trace +from google.devtools.cloudtrace_v2.types import tracing +from google.oauth2 import service_account +from google.protobuf import any_pb2 as gp_any # type: ignore +from google.protobuf import timestamp_pb2 as timestamp # type: ignore +from google.protobuf import wrappers_pb2 as wrappers # type: ignore +from google.rpc import status_pb2 as status # type: ignore + + +def client_cert_source_callback(): + return b"cert bytes", b"key bytes" + + +# If default endpoint is localhost, then default mtls endpoint will be the same. +# This method modifies the default endpoint so the client can produce a different +# mtls endpoint for endpoint testing purposes. +def modify_default_endpoint(client): + return ( + "foo.googleapis.com" + if ("localhost" in client.DEFAULT_ENDPOINT) + else client.DEFAULT_ENDPOINT + ) + + +def test__get_default_mtls_endpoint(): + api_endpoint = "example.googleapis.com" + api_mtls_endpoint = "example.mtls.googleapis.com" + sandbox_endpoint = "example.sandbox.googleapis.com" + sandbox_mtls_endpoint = "example.mtls.sandbox.googleapis.com" + non_googleapi = "api.example.com" + + assert TraceServiceClient._get_default_mtls_endpoint(None) is None + assert ( + TraceServiceClient._get_default_mtls_endpoint(api_endpoint) == api_mtls_endpoint + ) + assert ( + TraceServiceClient._get_default_mtls_endpoint(api_mtls_endpoint) + == api_mtls_endpoint + ) + assert ( + TraceServiceClient._get_default_mtls_endpoint(sandbox_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + TraceServiceClient._get_default_mtls_endpoint(sandbox_mtls_endpoint) + == sandbox_mtls_endpoint + ) + assert TraceServiceClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi + + +@pytest.mark.parametrize("client_class", [TraceServiceClient, TraceServiceAsyncClient,]) +def test_trace_service_client_from_service_account_info(client_class): + creds = credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_info" + ) as factory: + factory.return_value = creds + info = {"valid": True} + client = client_class.from_service_account_info(info) + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "cloudtrace.googleapis.com:443" + + +@pytest.mark.parametrize("client_class", [TraceServiceClient, TraceServiceAsyncClient,]) +def test_trace_service_client_from_service_account_file(client_class): + creds = credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_file" + ) as factory: + factory.return_value = creds + client = client_class.from_service_account_file("dummy/file/path.json") + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + client = client_class.from_service_account_json("dummy/file/path.json") + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "cloudtrace.googleapis.com:443" + + +def test_trace_service_client_get_transport_class(): + transport = TraceServiceClient.get_transport_class() + available_transports = [ + transports.TraceServiceGrpcTransport, + ] + assert transport in available_transports + + transport = TraceServiceClient.get_transport_class("grpc") + assert transport == transports.TraceServiceGrpcTransport + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (TraceServiceClient, transports.TraceServiceGrpcTransport, "grpc"), + ( + TraceServiceAsyncClient, + transports.TraceServiceGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +@mock.patch.object( + TraceServiceClient, "DEFAULT_ENDPOINT", modify_default_endpoint(TraceServiceClient) +) +@mock.patch.object( + TraceServiceAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(TraceServiceAsyncClient), +) +def test_trace_service_client_client_options( + client_class, transport_class, transport_name +): + # Check that if channel is provided we won't create a new one. + with mock.patch.object(TraceServiceClient, "get_transport_class") as gtc: + transport = transport_class(credentials=credentials.AnonymousCredentials()) + client = client_class(transport=transport) + gtc.assert_not_called() + + # Check that if channel is provided via str we will create a new one. + with mock.patch.object(TraceServiceClient, "get_transport_class") as gtc: + client = client_class(transport=transport_name) + gtc.assert_called() + + # Check the case api_endpoint is provided. + options = client_options.ClientOptions(api_endpoint="squid.clam.whelk") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is + # "never". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}): + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is + # "always". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}): + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_MTLS_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT has + # unsupported value. + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}): + with pytest.raises(MutualTLSChannelError): + client = client_class() + + # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} + ): + with pytest.raises(ValueError): + client = client_class() + + # Check the case quota_project_id is provided + options = client_options.ClientOptions(quota_project_id="octopus") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id="octopus", + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name,use_client_cert_env", + [ + (TraceServiceClient, transports.TraceServiceGrpcTransport, "grpc", "true"), + ( + TraceServiceAsyncClient, + transports.TraceServiceGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + (TraceServiceClient, transports.TraceServiceGrpcTransport, "grpc", "false"), + ( + TraceServiceAsyncClient, + transports.TraceServiceGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + TraceServiceClient, "DEFAULT_ENDPOINT", modify_default_endpoint(TraceServiceClient) +) +@mock.patch.object( + TraceServiceAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(TraceServiceAsyncClient), +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_trace_service_client_mtls_env_auto( + client_class, transport_class, transport_name, use_client_cert_env +): + # This tests the endpoint autoswitch behavior. Endpoint is autoswitched to the default + # mtls endpoint, if GOOGLE_API_USE_CLIENT_CERTIFICATE is "true" and client cert exists. + + # Check the case client_cert_source is provided. Whether client cert is used depends on + # GOOGLE_API_USE_CLIENT_CERTIFICATE value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + options = client_options.ClientOptions( + client_cert_source=client_cert_source_callback + ) + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + + if use_client_cert_env == "false": + expected_client_cert_source = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_client_cert_source = client_cert_source_callback + expected_host = client.DEFAULT_MTLS_ENDPOINT + + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case ADC client cert is provided. Whether client cert is used depends on + # GOOGLE_API_USE_CLIENT_CERTIFICATE value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=True, + ): + with mock.patch( + "google.auth.transport.mtls.default_client_cert_source", + return_value=client_cert_source_callback, + ): + if use_client_cert_env == "false": + expected_host = client.DEFAULT_ENDPOINT + expected_client_cert_source = None + else: + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_client_cert_source = client_cert_source_callback + + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=False, + ): + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (TraceServiceClient, transports.TraceServiceGrpcTransport, "grpc"), + ( + TraceServiceAsyncClient, + transports.TraceServiceGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_trace_service_client_client_options_scopes( + client_class, transport_class, transport_name +): + # Check the case scopes are provided. + options = client_options.ClientOptions(scopes=["1", "2"],) + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=["1", "2"], + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (TraceServiceClient, transports.TraceServiceGrpcTransport, "grpc"), + ( + TraceServiceAsyncClient, + transports.TraceServiceGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_trace_service_client_client_options_credentials_file( + client_class, transport_class, transport_name +): + # Check the case credentials file is provided. + options = client_options.ClientOptions(credentials_file="credentials.json") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file="credentials.json", + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_trace_service_client_client_options_from_dict(): + with mock.patch( + "google.devtools.cloudtrace_v2.services.trace_service.transports.TraceServiceGrpcTransport.__init__" + ) as grpc_transport: + grpc_transport.return_value = None + client = TraceServiceClient(client_options={"api_endpoint": "squid.clam.whelk"}) + grpc_transport.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_batch_write_spans( + transport: str = "grpc", request_type=tracing.BatchWriteSpansRequest +): + client = TraceServiceClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.batch_write_spans), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = None + + response = client.batch_write_spans(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == tracing.BatchWriteSpansRequest() + + # Establish that the response is the type that we expect. + assert response is None + + +def test_batch_write_spans_from_dict(): + test_batch_write_spans(request_type=dict) + + +def test_batch_write_spans_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = TraceServiceClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.batch_write_spans), "__call__" + ) as call: + client.batch_write_spans() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == tracing.BatchWriteSpansRequest() + + +@pytest.mark.asyncio +async def test_batch_write_spans_async( + transport: str = "grpc_asyncio", request_type=tracing.BatchWriteSpansRequest +): + client = TraceServiceAsyncClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.batch_write_spans), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + + response = await client.batch_write_spans(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == tracing.BatchWriteSpansRequest() + + # Establish that the response is the type that we expect. + assert response is None + + +@pytest.mark.asyncio +async def test_batch_write_spans_async_from_dict(): + await test_batch_write_spans_async(request_type=dict) + + +def test_batch_write_spans_field_headers(): + client = TraceServiceClient(credentials=credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = tracing.BatchWriteSpansRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.batch_write_spans), "__call__" + ) as call: + call.return_value = None + + client.batch_write_spans(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_batch_write_spans_field_headers_async(): + client = TraceServiceAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = tracing.BatchWriteSpansRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.batch_write_spans), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + + await client.batch_write_spans(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] + + +def test_batch_write_spans_flattened(): + client = TraceServiceClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.batch_write_spans), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = None + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.batch_write_spans( + name="name_value", spans=[trace.Span(name="name_value")], + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0].name == "name_value" + + assert args[0].spans == [trace.Span(name="name_value")] + + +def test_batch_write_spans_flattened_error(): + client = TraceServiceClient(credentials=credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.batch_write_spans( + tracing.BatchWriteSpansRequest(), + name="name_value", + spans=[trace.Span(name="name_value")], + ) + + +@pytest.mark.asyncio +async def test_batch_write_spans_flattened_async(): + client = TraceServiceAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.batch_write_spans), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = None + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.batch_write_spans( + name="name_value", spans=[trace.Span(name="name_value")], + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0].name == "name_value" + + assert args[0].spans == [trace.Span(name="name_value")] + + +@pytest.mark.asyncio +async def test_batch_write_spans_flattened_error_async(): + client = TraceServiceAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.batch_write_spans( + tracing.BatchWriteSpansRequest(), + name="name_value", + spans=[trace.Span(name="name_value")], + ) + + +def test_create_span(transport: str = "grpc", request_type=trace.Span): + client = TraceServiceClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_span), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = trace.Span( + name="name_value", + span_id="span_id_value", + parent_span_id="parent_span_id_value", + span_kind=trace.Span.SpanKind.INTERNAL, + ) + + response = client.create_span(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == trace.Span() + + # Establish that the response is the type that we expect. + + assert isinstance(response, trace.Span) + + assert response.name == "name_value" + + assert response.span_id == "span_id_value" + + assert response.parent_span_id == "parent_span_id_value" + + assert response.span_kind == trace.Span.SpanKind.INTERNAL + + +def test_create_span_from_dict(): + test_create_span(request_type=dict) + + +def test_create_span_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = TraceServiceClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_span), "__call__") as call: + client.create_span() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == trace.Span() + + +@pytest.mark.asyncio +async def test_create_span_async( + transport: str = "grpc_asyncio", request_type=trace.Span +): + client = TraceServiceAsyncClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_span), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + trace.Span( + name="name_value", + span_id="span_id_value", + parent_span_id="parent_span_id_value", + span_kind=trace.Span.SpanKind.INTERNAL, + ) + ) + + response = await client.create_span(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == trace.Span() + + # Establish that the response is the type that we expect. + assert isinstance(response, trace.Span) + + assert response.name == "name_value" + + assert response.span_id == "span_id_value" + + assert response.parent_span_id == "parent_span_id_value" + + assert response.span_kind == trace.Span.SpanKind.INTERNAL + + +@pytest.mark.asyncio +async def test_create_span_async_from_dict(): + await test_create_span_async(request_type=dict) + + +def test_create_span_field_headers(): + client = TraceServiceClient(credentials=credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = trace.Span() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_span), "__call__") as call: + call.return_value = trace.Span() + + client.create_span(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_create_span_field_headers_async(): + client = TraceServiceAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = trace.Span() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_span), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(trace.Span()) + + await client.create_span(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] + + +def test_credentials_transport_error(): + # It is an error to provide credentials and a transport instance. + transport = transports.TraceServiceGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = TraceServiceClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # It is an error to provide a credentials file and a transport instance. + transport = transports.TraceServiceGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = TraceServiceClient( + client_options={"credentials_file": "credentials.json"}, + transport=transport, + ) + + # It is an error to provide scopes and a transport instance. + transport = transports.TraceServiceGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = TraceServiceClient( + client_options={"scopes": ["1", "2"]}, transport=transport, + ) + + +def test_transport_instance(): + # A client may be instantiated with a custom transport instance. + transport = transports.TraceServiceGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + client = TraceServiceClient(transport=transport) + assert client.transport is transport + + +def test_transport_get_channel(): + # A client may be instantiated with a custom transport instance. + transport = transports.TraceServiceGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + transport = transports.TraceServiceGrpcAsyncIOTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + +@pytest.mark.parametrize( + "transport_class", + [ + transports.TraceServiceGrpcTransport, + transports.TraceServiceGrpcAsyncIOTransport, + ], +) +def test_transport_adc(transport_class): + # Test default credentials are used if not provided. + with mock.patch.object(auth, "default") as adc: + adc.return_value = (credentials.AnonymousCredentials(), None) + transport_class() + adc.assert_called_once() + + +def test_transport_grpc_default(): + # A client should use the gRPC transport by default. + client = TraceServiceClient(credentials=credentials.AnonymousCredentials(),) + assert isinstance(client.transport, transports.TraceServiceGrpcTransport,) + + +def test_trace_service_base_transport_error(): + # Passing both a credentials object and credentials_file should raise an error + with pytest.raises(exceptions.DuplicateCredentialArgs): + transport = transports.TraceServiceTransport( + credentials=credentials.AnonymousCredentials(), + credentials_file="credentials.json", + ) + + +def test_trace_service_base_transport(): + # Instantiate the base transport. + with mock.patch( + "google.devtools.cloudtrace_v2.services.trace_service.transports.TraceServiceTransport.__init__" + ) as Transport: + Transport.return_value = None + transport = transports.TraceServiceTransport( + credentials=credentials.AnonymousCredentials(), + ) + + # Every method on the transport should just blindly + # raise NotImplementedError. + methods = ( + "batch_write_spans", + "create_span", + ) + for method in methods: + with pytest.raises(NotImplementedError): + getattr(transport, method)(request=object()) + + +def test_trace_service_base_transport_with_credentials_file(): + # Instantiate the base transport with a credentials file + with mock.patch.object( + auth, "load_credentials_from_file" + ) as load_creds, mock.patch( + "google.devtools.cloudtrace_v2.services.trace_service.transports.TraceServiceTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + load_creds.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.TraceServiceTransport( + credentials_file="credentials.json", quota_project_id="octopus", + ) + load_creds.assert_called_once_with( + "credentials.json", + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/trace.append", + ), + quota_project_id="octopus", + ) + + +def test_trace_service_base_transport_with_adc(): + # Test the default credentials are used if credentials and credentials_file are None. + with mock.patch.object(auth, "default") as adc, mock.patch( + "google.devtools.cloudtrace_v2.services.trace_service.transports.TraceServiceTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.TraceServiceTransport() + adc.assert_called_once() + + +def test_trace_service_auth_adc(): + # If no credentials are provided, we should use ADC credentials. + with mock.patch.object(auth, "default") as adc: + adc.return_value = (credentials.AnonymousCredentials(), None) + TraceServiceClient() + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/trace.append", + ), + quota_project_id=None, + ) + + +def test_trace_service_transport_auth_adc(): + # If credentials and host are not provided, the transport class should use + # ADC credentials. + with mock.patch.object(auth, "default") as adc: + adc.return_value = (credentials.AnonymousCredentials(), None) + transports.TraceServiceGrpcTransport( + host="squid.clam.whelk", quota_project_id="octopus" + ) + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/trace.append", + ), + quota_project_id="octopus", + ) + + +@pytest.mark.parametrize( + "transport_class", + [transports.TraceServiceGrpcTransport, transports.TraceServiceGrpcAsyncIOTransport], +) +def test_trace_service_grpc_transport_client_cert_source_for_mtls(transport_class): + cred = credentials.AnonymousCredentials() + + # Check ssl_channel_credentials is used if provided. + with mock.patch.object(transport_class, "create_channel") as mock_create_channel: + mock_ssl_channel_creds = mock.Mock() + transport_class( + host="squid.clam.whelk", + credentials=cred, + ssl_channel_credentials=mock_ssl_channel_creds, + ) + mock_create_channel.assert_called_once_with( + "squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/trace.append", + ), + ssl_credentials=mock_ssl_channel_creds, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Check if ssl_channel_credentials is not provided, then client_cert_source_for_mtls + # is used. + with mock.patch.object(transport_class, "create_channel", return_value=mock.Mock()): + with mock.patch("grpc.ssl_channel_credentials") as mock_ssl_cred: + transport_class( + credentials=cred, + client_cert_source_for_mtls=client_cert_source_callback, + ) + expected_cert, expected_key = client_cert_source_callback() + mock_ssl_cred.assert_called_once_with( + certificate_chain=expected_cert, private_key=expected_key + ) + + +def test_trace_service_host_no_port(): + client = TraceServiceClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="cloudtrace.googleapis.com" + ), + ) + assert client.transport._host == "cloudtrace.googleapis.com:443" + + +def test_trace_service_host_with_port(): + client = TraceServiceClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="cloudtrace.googleapis.com:8000" + ), + ) + assert client.transport._host == "cloudtrace.googleapis.com:8000" + + +def test_trace_service_grpc_transport_channel(): + channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) + + # Check that channel is used if provided. + transport = transports.TraceServiceGrpcTransport( + host="squid.clam.whelk", channel=channel, + ) + assert transport.grpc_channel == channel + assert transport._host == "squid.clam.whelk:443" + assert transport._ssl_channel_credentials == None + + +def test_trace_service_grpc_asyncio_transport_channel(): + channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) + + # Check that channel is used if provided. + transport = transports.TraceServiceGrpcAsyncIOTransport( + host="squid.clam.whelk", channel=channel, + ) + assert transport.grpc_channel == channel + assert transport._host == "squid.clam.whelk:443" + assert transport._ssl_channel_credentials == None + + +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. +@pytest.mark.parametrize( + "transport_class", + [transports.TraceServiceGrpcTransport, transports.TraceServiceGrpcAsyncIOTransport], +) +def test_trace_service_transport_channel_mtls_with_client_cert_source(transport_class): + with mock.patch( + "grpc.ssl_channel_credentials", autospec=True + ) as grpc_ssl_channel_cred: + with mock.patch.object( + transport_class, "create_channel" + ) as grpc_create_channel: + mock_ssl_cred = mock.Mock() + grpc_ssl_channel_cred.return_value = mock_ssl_cred + + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + + cred = credentials.AnonymousCredentials() + with pytest.warns(DeprecationWarning): + with mock.patch.object(auth, "default") as adc: + adc.return_value = (cred, None) + transport = transport_class( + host="squid.clam.whelk", + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=client_cert_source_callback, + ) + adc.assert_called_once() + + grpc_ssl_channel_cred.assert_called_once_with( + certificate_chain=b"cert bytes", private_key=b"key bytes" + ) + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/trace.append", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred + + +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. +@pytest.mark.parametrize( + "transport_class", + [transports.TraceServiceGrpcTransport, transports.TraceServiceGrpcAsyncIOTransport], +) +def test_trace_service_transport_channel_mtls_with_adc(transport_class): + mock_ssl_cred = mock.Mock() + with mock.patch.multiple( + "google.auth.transport.grpc.SslCredentials", + __init__=mock.Mock(return_value=None), + ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), + ): + with mock.patch.object( + transport_class, "create_channel" + ) as grpc_create_channel: + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + mock_cred = mock.Mock() + + with pytest.warns(DeprecationWarning): + transport = transport_class( + host="squid.clam.whelk", + credentials=mock_cred, + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=None, + ) + + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=mock_cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/trace.append", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + assert transport.grpc_channel == mock_grpc_channel + + +def test_span_path(): + project = "squid" + trace = "clam" + span = "whelk" + + expected = "projects/{project}/traces/{trace}/spans/{span}".format( + project=project, trace=trace, span=span, + ) + actual = TraceServiceClient.span_path(project, trace, span) + assert expected == actual + + +def test_parse_span_path(): + expected = { + "project": "octopus", + "trace": "oyster", + "span": "nudibranch", + } + path = TraceServiceClient.span_path(**expected) + + # Check that the path construction is reversible. + actual = TraceServiceClient.parse_span_path(path) + assert expected == actual + + +def test_common_billing_account_path(): + billing_account = "cuttlefish" + + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = TraceServiceClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "mussel", + } + path = TraceServiceClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = TraceServiceClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "winkle" + + expected = "folders/{folder}".format(folder=folder,) + actual = TraceServiceClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "nautilus", + } + path = TraceServiceClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = TraceServiceClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "scallop" + + expected = "organizations/{organization}".format(organization=organization,) + actual = TraceServiceClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "abalone", + } + path = TraceServiceClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = TraceServiceClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "squid" + + expected = "projects/{project}".format(project=project,) + actual = TraceServiceClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "clam", + } + path = TraceServiceClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = TraceServiceClient.parse_common_project_path(path) + assert expected == actual + + +def test_common_location_path(): + project = "whelk" + location = "octopus" + + expected = "projects/{project}/locations/{location}".format( + project=project, location=location, + ) + actual = TraceServiceClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "oyster", + "location": "nudibranch", + } + path = TraceServiceClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = TraceServiceClient.parse_common_location_path(path) + assert expected == actual + + +def test_client_withDEFAULT_CLIENT_INFO(): + client_info = gapic_v1.client_info.ClientInfo() + + with mock.patch.object( + transports.TraceServiceTransport, "_prep_wrapped_messages" + ) as prep: + client = TraceServiceClient( + credentials=credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) + + with mock.patch.object( + transports.TraceServiceTransport, "_prep_wrapped_messages" + ) as prep: + transport_class = TraceServiceClient.get_transport_class() + transport = transport_class( + credentials=credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) From 645ec3e68f142dc53a8ca32813f7e34acf34dde8 Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:06:10 -0700 Subject: [PATCH 02/47] chore(py-library): enable snippet-bot Co-authored-by: Benjamin E. Coe Source-Author: Takashi Matsuo Source-Date: Tue Sep 1 17:14:08 2020 +0000 Source-Repo: googleapis/synthtool Source-Sha: d91dd8aac77f7a9c5506c238038a26fa4f9e361e Source-Link: https://github.com/googleapis/synthtool/commit/d91dd8aac77f7a9c5506c238038a26fa4f9e361e --- .github/snippet-bot.yml | 0 synth.metadata | 5 +++-- 2 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 .github/snippet-bot.yml diff --git a/.github/snippet-bot.yml b/.github/snippet-bot.yml new file mode 100644 index 00000000..e69de29b diff --git a/synth.metadata b/synth.metadata index 75601243..0f086a33 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "05de3e1e14a0b07eab8b474e669164dbd31f81fb" + "sha": "d91dd8aac77f7a9c5506c238038a26fa4f9e361e" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "05de3e1e14a0b07eab8b474e669164dbd31f81fb" + "sha": "d91dd8aac77f7a9c5506c238038a26fa4f9e361e" } } ], @@ -59,6 +59,7 @@ ".github/ISSUE_TEMPLATE/support_request.md", ".github/PULL_REQUEST_TEMPLATE.md", ".github/release-please.yml", + ".github/snippet-bot.yml", ".gitignore", ".kokoro/build.sh", ".kokoro/continuous/common.cfg", From bf3560c251b34cb4dd69a662b6261ba53609cbde Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:06:56 -0700 Subject: [PATCH 03/47] chore(py-library): update decrypt secrets file * chore(py-library): update decrypt secrets file From https://github.com/GoogleCloudPlatform/python-docs-samples/blob/master/scripts/decrypt-secrets.sh * docs: explain conditional Co-authored-by: Jeffrey Rennie Source-Author: Bu Sun Kim <8822365+busunkim96@users.noreply.github.com> Source-Date: Tue Sep 8 11:35:59 2020 -0600 Source-Repo: googleapis/synthtool Source-Sha: d302f93d7f47e2852e585ac35ab2d15585717ec0 Source-Link: https://github.com/googleapis/synthtool/commit/d302f93d7f47e2852e585ac35ab2d15585717ec0 --- scripts/decrypt-secrets.sh | 15 ++++++++++++++- synth.metadata | 4 ++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/scripts/decrypt-secrets.sh b/scripts/decrypt-secrets.sh index ff599eb2..21f6d2a2 100755 --- a/scripts/decrypt-secrets.sh +++ b/scripts/decrypt-secrets.sh @@ -20,14 +20,27 @@ ROOT=$( dirname "$DIR" ) # Work from the project root. cd $ROOT +# Prevent it from overriding files. +# We recommend that sample authors use their own service account files and cloud project. +# In that case, they are supposed to prepare these files by themselves. +if [[ -f "testing/test-env.sh" ]] || \ + [[ -f "testing/service-account.json" ]] || \ + [[ -f "testing/client-secrets.json" ]]; then + echo "One or more target files exist, aborting." + exit 1 +fi + # Use SECRET_MANAGER_PROJECT if set, fallback to cloud-devrel-kokoro-resources. PROJECT_ID="${SECRET_MANAGER_PROJECT:-cloud-devrel-kokoro-resources}" gcloud secrets versions access latest --secret="python-docs-samples-test-env" \ + --project="${PROJECT_ID}" \ > testing/test-env.sh gcloud secrets versions access latest \ --secret="python-docs-samples-service-account" \ + --project="${PROJECT_ID}" \ > testing/service-account.json gcloud secrets versions access latest \ --secret="python-docs-samples-client-secrets" \ - > testing/client-secrets.json \ No newline at end of file + --project="${PROJECT_ID}" \ + > testing/client-secrets.json diff --git a/synth.metadata b/synth.metadata index 0f086a33..9e63397c 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "d91dd8aac77f7a9c5506c238038a26fa4f9e361e" + "sha": "d302f93d7f47e2852e585ac35ab2d15585717ec0" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "d91dd8aac77f7a9c5506c238038a26fa4f9e361e" + "sha": "d302f93d7f47e2852e585ac35ab2d15585717ec0" } } ], From f984e81ccce7572b1090d788ffca5453d212645c Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:07:19 -0700 Subject: [PATCH 04/47] chore(python-library): use sphinx 1.5.5 for the docfx job Originally tested at: https://github.com/googleapis/python-texttospeech/pull/89 This change will fix the missing docstring in the yaml files. Source-Author: Takashi Matsuo Source-Date: Thu Sep 10 04:12:14 2020 +0000 Source-Repo: googleapis/synthtool Source-Sha: ffcee7952b74f647cbb3ef021d95422f10816fca Source-Link: https://github.com/googleapis/synthtool/commit/ffcee7952b74f647cbb3ef021d95422f10816fca --- docs/conf.py | 2 +- noxfile.py | 4 +++- synth.metadata | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 6345740f..5b4e26e7 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -29,7 +29,7 @@ # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. -needs_sphinx = "1.6.3" +needs_sphinx = "1.5.5" # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom diff --git a/noxfile.py b/noxfile.py index b1de6b92..1b2bd247 100644 --- a/noxfile.py +++ b/noxfile.py @@ -173,7 +173,9 @@ def docfx(session): """Build the docfx yaml files for this library.""" session.install("-e", ".") - session.install("sphinx", "alabaster", "recommonmark", "sphinx-docfx-yaml") + # sphinx-docfx-yaml supports up to sphinx version 1.5.5. + # https://github.com/docascode/sphinx-docfx-yaml/issues/97 + session.install("sphinx==1.5.5", "alabaster", "recommonmark", "sphinx-docfx-yaml") shutil.rmtree(os.path.join("docs", "_build"), ignore_errors=True) session.run( diff --git a/synth.metadata b/synth.metadata index 9e63397c..2bd7d987 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "d302f93d7f47e2852e585ac35ab2d15585717ec0" + "sha": "ffcee7952b74f647cbb3ef021d95422f10816fca" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "d302f93d7f47e2852e585ac35ab2d15585717ec0" + "sha": "ffcee7952b74f647cbb3ef021d95422f10816fca" } } ], From 5e6dc2ae47ff1f10d0f15cafe712f32107d878e8 Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:07:30 -0700 Subject: [PATCH 05/47] =?UTF-8?q?build(python):=20use=20release-publish=20?= =?UTF-8?q?app=20for=20notifying=20GitHub=20of=20releas=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * build(python): use release-publish app for notifying GitHub of release status * fix: re-add pypi password Source-Author: Bu Sun Kim <8822365+busunkim96@users.noreply.github.com> Source-Date: Wed Sep 16 08:46:42 2020 -0600 Source-Repo: googleapis/synthtool Source-Sha: 257fda18168bedb76985024bd198ed1725485488 Source-Link: https://github.com/googleapis/synthtool/commit/257fda18168bedb76985024bd198ed1725485488 --- .kokoro/release/common.cfg | 50 ++++++++++---------------------------- synth.metadata | 4 +-- 2 files changed, 15 insertions(+), 39 deletions(-) diff --git a/.kokoro/release/common.cfg b/.kokoro/release/common.cfg index b3df8aba..f1e287cb 100644 --- a/.kokoro/release/common.cfg +++ b/.kokoro/release/common.cfg @@ -23,42 +23,18 @@ env_vars: { value: "github/python-trace/.kokoro/release.sh" } -# Fetch the token needed for reporting release status to GitHub -before_action { - fetch_keystore { - keystore_resource { - keystore_config_id: 73713 - keyname: "yoshi-automation-github-key" - } - } -} - -# Fetch PyPI password -before_action { - fetch_keystore { - keystore_resource { - keystore_config_id: 73713 - keyname: "google_cloud_pypi_password" - } - } -} - -# Fetch magictoken to use with Magic Github Proxy -before_action { - fetch_keystore { - keystore_resource { - keystore_config_id: 73713 - keyname: "releasetool-magictoken" - } - } +# Fetch PyPI password +before_action { + fetch_keystore { + keystore_resource { + keystore_config_id: 73713 + keyname: "google_cloud_pypi_password" + } + } } -# Fetch api key to use with Magic Github Proxy -before_action { - fetch_keystore { - keystore_resource { - keystore_config_id: 73713 - keyname: "magic-github-proxy-api-key" - } - } -} +# Tokens needed to report release status back to GitHub +env_vars: { + key: "SECRET_MANAGER_KEYS" + value: "releasetool-publish-reporter-app,releasetool-publish-reporter-googleapis-installation,releasetool-publish-reporter-pem" +} \ No newline at end of file diff --git a/synth.metadata b/synth.metadata index 2bd7d987..764cc7be 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "ffcee7952b74f647cbb3ef021d95422f10816fca" + "sha": "257fda18168bedb76985024bd198ed1725485488" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "ffcee7952b74f647cbb3ef021d95422f10816fca" + "sha": "257fda18168bedb76985024bd198ed1725485488" } } ], From 2dacacfefefd4ad44acf62a0e9e3ff30501b7134 Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:07:42 -0700 Subject: [PATCH 06/47] build(python): add secret manager in kokoro Source-Author: Bu Sun Kim <8822365+busunkim96@users.noreply.github.com> Source-Date: Wed Sep 16 10:24:40 2020 -0600 Source-Repo: googleapis/synthtool Source-Sha: dba48bb9bc6959c232bec9150ac6313b608fe7bd Source-Link: https://github.com/googleapis/synthtool/commit/dba48bb9bc6959c232bec9150ac6313b608fe7bd --- .kokoro/populate-secrets.sh | 43 +++++++++++++++++++++++++++++++++++++ .kokoro/trampoline.sh | 15 ++++++++----- synth.metadata | 5 +++-- 3 files changed, 56 insertions(+), 7 deletions(-) create mode 100755 .kokoro/populate-secrets.sh diff --git a/.kokoro/populate-secrets.sh b/.kokoro/populate-secrets.sh new file mode 100755 index 00000000..f5251425 --- /dev/null +++ b/.kokoro/populate-secrets.sh @@ -0,0 +1,43 @@ +#!/bin/bash +# Copyright 2020 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. + +set -eo pipefail + +function now { date +"%Y-%m-%d %H:%M:%S" | tr -d '\n' ;} +function msg { println "$*" >&2 ;} +function println { printf '%s\n' "$(now) $*" ;} + + +# Populates requested secrets set in SECRET_MANAGER_KEYS from service account: +# kokoro-trampoline@cloud-devrel-kokoro-resources.iam.gserviceaccount.com +SECRET_LOCATION="${KOKORO_GFILE_DIR}/secret_manager" +msg "Creating folder on disk for secrets: ${SECRET_LOCATION}" +mkdir -p ${SECRET_LOCATION} +for key in $(echo ${SECRET_MANAGER_KEYS} | sed "s/,/ /g") +do + msg "Retrieving secret ${key}" + docker run --entrypoint=gcloud \ + --volume=${KOKORO_GFILE_DIR}:${KOKORO_GFILE_DIR} \ + gcr.io/google.com/cloudsdktool/cloud-sdk \ + secrets versions access latest \ + --project cloud-devrel-kokoro-resources \ + --secret ${key} > \ + "${SECRET_LOCATION}/${key}" + if [[ $? == 0 ]]; then + msg "Secret written to ${SECRET_LOCATION}/${key}" + else + msg "Error retrieving secret ${key}" + fi +done diff --git a/.kokoro/trampoline.sh b/.kokoro/trampoline.sh index e8c4251f..f39236e9 100755 --- a/.kokoro/trampoline.sh +++ b/.kokoro/trampoline.sh @@ -15,9 +15,14 @@ set -eo pipefail -python3 "${KOKORO_GFILE_DIR}/trampoline_v1.py" || ret_code=$? +# Always run the cleanup script, regardless of the success of bouncing into +# the container. +function cleanup() { + chmod +x ${KOKORO_GFILE_DIR}/trampoline_cleanup.sh + ${KOKORO_GFILE_DIR}/trampoline_cleanup.sh + echo "cleanup"; +} +trap cleanup EXIT -chmod +x ${KOKORO_GFILE_DIR}/trampoline_cleanup.sh -${KOKORO_GFILE_DIR}/trampoline_cleanup.sh || true - -exit ${ret_code} +$(dirname $0)/populate-secrets.sh # Secret Manager secrets. +python3 "${KOKORO_GFILE_DIR}/trampoline_v1.py" \ No newline at end of file diff --git a/synth.metadata b/synth.metadata index 764cc7be..96823d04 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "257fda18168bedb76985024bd198ed1725485488" + "sha": "dba48bb9bc6959c232bec9150ac6313b608fe7bd" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "257fda18168bedb76985024bd198ed1725485488" + "sha": "dba48bb9bc6959c232bec9150ac6313b608fe7bd" } } ], @@ -69,6 +69,7 @@ ".kokoro/docs/common.cfg", ".kokoro/docs/docs-presubmit.cfg", ".kokoro/docs/docs.cfg", + ".kokoro/populate-secrets.sh", ".kokoro/presubmit/common.cfg", ".kokoro/presubmit/presubmit.cfg", ".kokoro/publish-docs.sh", From 5d5d324d31209a70a44577e38ee050630e9a3f5a Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:08:29 -0700 Subject: [PATCH 07/47] chore(python): add sphinx doctest extension Source-Author: Bu Sun Kim <8822365+busunkim96@users.noreply.github.com> Source-Date: Mon Sep 21 13:09:57 2020 -0600 Source-Repo: googleapis/synthtool Source-Sha: 27f4406999b1eee29e04b09b2423a8e4646c7e24 Source-Link: https://github.com/googleapis/synthtool/commit/27f4406999b1eee29e04b09b2423a8e4646c7e24 --- docs/conf.py | 1 + synth.metadata | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 5b4e26e7..58f6dd24 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -39,6 +39,7 @@ "sphinx.ext.autosummary", "sphinx.ext.intersphinx", "sphinx.ext.coverage", + "sphinx.ext.doctest", "sphinx.ext.napoleon", "sphinx.ext.todo", "sphinx.ext.viewcode", diff --git a/synth.metadata b/synth.metadata index 96823d04..5fa43a3d 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "dba48bb9bc6959c232bec9150ac6313b608fe7bd" + "sha": "27f4406999b1eee29e04b09b2423a8e4646c7e24" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "dba48bb9bc6959c232bec9150ac6313b608fe7bd" + "sha": "27f4406999b1eee29e04b09b2423a8e4646c7e24" } } ], From 88a45713741aa950ac77f827da969cc567a5b9cd Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:08:53 -0700 Subject: [PATCH 08/47] chore(python): remove note about editable installs `pip install -e .` is supported and is how we install the library for tests. Source-Author: Bu Sun Kim <8822365+busunkim96@users.noreply.github.com> Source-Date: Tue Sep 22 12:06:12 2020 -0600 Source-Repo: googleapis/synthtool Source-Sha: a651c5fb763c69a921aecdd3e1d8dc51dbf20f8d Source-Link: https://github.com/googleapis/synthtool/commit/a651c5fb763c69a921aecdd3e1d8dc51dbf20f8d --- CONTRIBUTING.rst | 19 ------------------- synth.metadata | 4 ++-- 2 files changed, 2 insertions(+), 21 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 0e17c793..d6c460d6 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -80,25 +80,6 @@ We use `nox `__ to instrument our tests. .. nox: https://pypi.org/project/nox/ -Note on Editable Installs / Develop Mode -======================================== - -- As mentioned previously, using ``setuptools`` in `develop mode`_ - or a ``pip`` `editable install`_ is not possible with this - library. This is because this library uses `namespace packages`_. - For context see `Issue #2316`_ and the relevant `PyPA issue`_. - - Since ``editable`` / ``develop`` mode can't be used, packages - need to be installed directly. Hence your changes to the source - tree don't get incorporated into the **already installed** - package. - -.. _namespace packages: https://www.python.org/dev/peps/pep-0420/ -.. _Issue #2316: https://github.com/GoogleCloudPlatform/google-cloud-python/issues/2316 -.. _PyPA issue: https://github.com/pypa/packaging-problems/issues/12 -.. _develop mode: https://setuptools.readthedocs.io/en/latest/setuptools.html#development-mode -.. _editable install: https://pip.pypa.io/en/stable/reference/pip_install/#editable-installs - ***************************************** I'm getting weird errors... Can you help? ***************************************** diff --git a/synth.metadata b/synth.metadata index 5fa43a3d..a55edbb2 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "27f4406999b1eee29e04b09b2423a8e4646c7e24" + "sha": "a651c5fb763c69a921aecdd3e1d8dc51dbf20f8d" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "27f4406999b1eee29e04b09b2423a8e4646c7e24" + "sha": "a651c5fb763c69a921aecdd3e1d8dc51dbf20f8d" } } ], From 786b49efa8bf849d6e71f24a6e9347bcd4713824 Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:09:41 -0700 Subject: [PATCH 09/47] chore(python): use BUILD_SPECIFIC_GCLOUD_PROJECT for samples https://github.com/googleapis/python-talent/blob/ef045e8eb348db36d7a2a611e6f26b11530d273b/samples/snippets/noxfile_config.py#L27-L32 `BUILD_SPECIFIC_GCLOUD_PROJECT` is an alternate project used for sample tests that do poorly with concurrent runs on the same project. Source-Author: Bu Sun Kim <8822365+busunkim96@users.noreply.github.com> Source-Date: Wed Sep 30 13:06:03 2020 -0600 Source-Repo: googleapis/synthtool Source-Sha: 9b0da5204ab90bcc36f8cd4e5689eff1a54cc3e4 Source-Link: https://github.com/googleapis/synthtool/commit/9b0da5204ab90bcc36f8cd4e5689eff1a54cc3e4 --- .kokoro/samples/python3.6/common.cfg | 6 ++++++ .kokoro/samples/python3.7/common.cfg | 6 ++++++ .kokoro/samples/python3.8/common.cfg | 6 ++++++ synth.metadata | 4 ++-- 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/.kokoro/samples/python3.6/common.cfg b/.kokoro/samples/python3.6/common.cfg index 671d6605..74a50800 100644 --- a/.kokoro/samples/python3.6/common.cfg +++ b/.kokoro/samples/python3.6/common.cfg @@ -13,6 +13,12 @@ env_vars: { value: "py-3.6" } +# Declare build specific Cloud project. +env_vars: { + key: "BUILD_SPECIFIC_GCLOUD_PROJECT" + value: "python-docs-samples-tests-py36" +} + env_vars: { key: "TRAMPOLINE_BUILD_FILE" value: "github/python-trace/.kokoro/test-samples.sh" diff --git a/.kokoro/samples/python3.7/common.cfg b/.kokoro/samples/python3.7/common.cfg index cf504173..8549f421 100644 --- a/.kokoro/samples/python3.7/common.cfg +++ b/.kokoro/samples/python3.7/common.cfg @@ -13,6 +13,12 @@ env_vars: { value: "py-3.7" } +# Declare build specific Cloud project. +env_vars: { + key: "BUILD_SPECIFIC_GCLOUD_PROJECT" + value: "python-docs-samples-tests-py37" +} + env_vars: { key: "TRAMPOLINE_BUILD_FILE" value: "github/python-trace/.kokoro/test-samples.sh" diff --git a/.kokoro/samples/python3.8/common.cfg b/.kokoro/samples/python3.8/common.cfg index a0a3be93..74fb9be8 100644 --- a/.kokoro/samples/python3.8/common.cfg +++ b/.kokoro/samples/python3.8/common.cfg @@ -13,6 +13,12 @@ env_vars: { value: "py-3.8" } +# Declare build specific Cloud project. +env_vars: { + key: "BUILD_SPECIFIC_GCLOUD_PROJECT" + value: "python-docs-samples-tests-py38" +} + env_vars: { key: "TRAMPOLINE_BUILD_FILE" value: "github/python-trace/.kokoro/test-samples.sh" diff --git a/synth.metadata b/synth.metadata index a55edbb2..73d64779 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "a651c5fb763c69a921aecdd3e1d8dc51dbf20f8d" + "sha": "9b0da5204ab90bcc36f8cd4e5689eff1a54cc3e4" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "a651c5fb763c69a921aecdd3e1d8dc51dbf20f8d" + "sha": "9b0da5204ab90bcc36f8cd4e5689eff1a54cc3e4" } } ], From 04cf383efc58ce88cf6b2ee95e13dfa58e9d5b62 Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:10:17 -0700 Subject: [PATCH 10/47] chore(python): use 'setup.py' to detect repo root Closes #792 Source-Author: Bu Sun Kim <8822365+busunkim96@users.noreply.github.com> Source-Date: Fri Oct 9 15:06:33 2020 -0600 Source-Repo: googleapis/synthtool Source-Sha: e0ae456852bf22f38796deb79cff30b516fde244 Source-Link: https://github.com/googleapis/synthtool/commit/e0ae456852bf22f38796deb79cff30b516fde244 --- samples/snippets/noxfile.py | 5 +++++ synth.metadata | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/samples/snippets/noxfile.py b/samples/snippets/noxfile.py index ba55d7ce..01686e4a 100644 --- a/samples/snippets/noxfile.py +++ b/samples/snippets/noxfile.py @@ -201,6 +201,11 @@ def _get_repo_root(): break if Path(p / ".git").exists(): return str(p) + # .git is not available in repos cloned via Cloud Build + # setup.py is always in the library's root, so use that instead + # https://github.com/googleapis/synthtool/issues/792 + if Path(p / "setup.py").exists(): + return str(p) p = p.parent raise Exception("Unable to detect repository root.") diff --git a/synth.metadata b/synth.metadata index 73d64779..78c306b6 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "9b0da5204ab90bcc36f8cd4e5689eff1a54cc3e4" + "sha": "e0ae456852bf22f38796deb79cff30b516fde244" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "9b0da5204ab90bcc36f8cd4e5689eff1a54cc3e4" + "sha": "e0ae456852bf22f38796deb79cff30b516fde244" } } ], From 8fc6683db6f45a34b4135d1d68c0686edc289fa9 Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:11:17 -0700 Subject: [PATCH 11/47] build(python): samples tests should pass if no samples exist Source-Author: Daniel Sanche Source-Date: Wed Oct 14 08:00:06 2020 -0700 Source-Repo: googleapis/synthtool Source-Sha: 477764cc4ee6db346d3febef2bb1ea0abf27de52 Source-Link: https://github.com/googleapis/synthtool/commit/477764cc4ee6db346d3febef2bb1ea0abf27de52 --- .kokoro/test-samples.sh | 8 +++++++- synth.metadata | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.kokoro/test-samples.sh b/.kokoro/test-samples.sh index 140899ff..8ab944ec 100755 --- a/.kokoro/test-samples.sh +++ b/.kokoro/test-samples.sh @@ -28,6 +28,12 @@ if [[ $KOKORO_BUILD_ARTIFACTS_SUBDIR = *"periodic"* ]]; then git checkout $LATEST_RELEASE fi +# Exit early if samples directory doesn't exist +if [ ! -d "./samples" ]; then + echo "No tests run. `./samples` not found" + exit 0 +fi + # Disable buffering, so that the logs stream through. export PYTHONUNBUFFERED=1 @@ -101,4 +107,4 @@ cd "$ROOT" # Workaround for Kokoro permissions issue: delete secrets rm testing/{test-env.sh,client-secrets.json,service-account.json} -exit "$RTN" \ No newline at end of file +exit "$RTN" diff --git a/synth.metadata b/synth.metadata index 78c306b6..3a1b28cd 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "e0ae456852bf22f38796deb79cff30b516fde244" + "sha": "477764cc4ee6db346d3febef2bb1ea0abf27de52" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "e0ae456852bf22f38796deb79cff30b516fde244" + "sha": "477764cc4ee6db346d3febef2bb1ea0abf27de52" } } ], From 9ca4abb81b64a8664e65866170d8e21cfb75f54e Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:11:53 -0700 Subject: [PATCH 12/47] chore(python_library): change the docs bucket name Source-Author: Takashi Matsuo Source-Date: Fri Oct 16 09:58:05 2020 -0700 Source-Repo: googleapis/synthtool Source-Sha: da5c6050d13b4950c82666a81d8acd25157664ae Source-Link: https://github.com/googleapis/synthtool/commit/da5c6050d13b4950c82666a81d8acd25157664ae --- .kokoro/docs/common.cfg | 2 +- synth.metadata | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.kokoro/docs/common.cfg b/.kokoro/docs/common.cfg index 924821ed..015f2f6b 100644 --- a/.kokoro/docs/common.cfg +++ b/.kokoro/docs/common.cfg @@ -30,7 +30,7 @@ env_vars: { env_vars: { key: "V2_STAGING_BUCKET" - value: "docs-staging-v2-staging" + value: "docs-staging-v2" } # It will upload the docker image after successful builds. diff --git a/synth.metadata b/synth.metadata index 3a1b28cd..29c86815 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "477764cc4ee6db346d3febef2bb1ea0abf27de52" + "sha": "da5c6050d13b4950c82666a81d8acd25157664ae" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "477764cc4ee6db346d3febef2bb1ea0abf27de52" + "sha": "da5c6050d13b4950c82666a81d8acd25157664ae" } } ], From 2babfa96cd60fd1c64186e1a1401d41a110b78c8 Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:12:39 -0700 Subject: [PATCH 13/47] chore(docs): update code of conduct of synthtool and templates Source-Author: Christopher Wilcox Source-Date: Thu Oct 22 14:22:01 2020 -0700 Source-Repo: googleapis/synthtool Source-Sha: 5f6ef0ec5501d33c4667885b37a7685a30d41a76 Source-Link: https://github.com/googleapis/synthtool/commit/5f6ef0ec5501d33c4667885b37a7685a30d41a76 --- CODE_OF_CONDUCT.md | 123 ++++++++++++++++++++++++++++++++------------- synth.metadata | 4 +- 2 files changed, 89 insertions(+), 38 deletions(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index b3d1f602..039f4368 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,44 +1,95 @@ -# Contributor Code of Conduct +# Code of Conduct -As contributors and maintainers of this project, -and in the interest of fostering an open and welcoming community, -we pledge to respect all people who contribute through reporting issues, -posting feature requests, updating documentation, -submitting pull requests or patches, and other activities. +## Our Pledge -We are committed to making participation in this project -a harassment-free experience for everyone, -regardless of level of experience, gender, gender identity and expression, -sexual orientation, disability, personal appearance, -body size, race, ethnicity, age, religion, or nationality. +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of +experience, education, socio-economic status, nationality, personal appearance, +race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members Examples of unacceptable behavior by participants include: -* The use of sexualized language or imagery -* Personal attacks -* Trolling or insulting/derogatory comments -* Public or private harassment -* Publishing other's private information, -such as physical or electronic -addresses, without explicit permission -* Other unethical or unprofessional conduct. +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject -comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct. -By adopting this Code of Conduct, -project maintainers commit themselves to fairly and consistently -applying these principles to every aspect of managing this project. -Project maintainers who do not follow or enforce the Code of Conduct -may be permanently removed from the project team. - -This code of conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. - -Instances of abusive, harassing, or otherwise unacceptable behavior -may be reported by opening an issue -or contacting one or more of the project maintainers. - -This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.2.0, -available at [http://contributor-covenant.org/version/1/2/0/](http://contributor-covenant.org/version/1/2/0/) +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, or to ban temporarily or permanently any +contributor for other behaviors that they deem inappropriate, threatening, +offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +This Code of Conduct also applies outside the project spaces when the Project +Steward has a reasonable belief that an individual's behavior may have a +negative impact on the project or its community. + +## Conflict Resolution + +We do not believe that all conflict is bad; healthy debate and disagreement +often yield positive results. However, it is never okay to be disrespectful or +to engage in behavior that violates the project’s code of conduct. + +If you see someone violating the code of conduct, you are encouraged to address +the behavior directly with those involved. Many issues can be resolved quickly +and easily, and this gives people more control over the outcome of their +dispute. If you are unable to resolve the matter for any reason, or if the +behavior is threatening or harassing, report it. We are dedicated to providing +an environment where participants feel welcome and safe. + + +Reports should be directed to *googleapis-stewards@google.com*, the +Project Steward(s) for *Google Cloud Client Libraries*. It is the Project Steward’s duty to +receive and address reported violations of the code of conduct. They will then +work with a committee consisting of representatives from the Open Source +Programs Office and the Google Open Source Strategy team. If for any reason you +are uncomfortable reaching out to the Project Steward, please email +opensource@google.com. + +We will investigate every complaint, but you may not receive a direct response. +We will use our discretion in determining when and how to follow up on reported +incidents, which may range from not taking action to permanent expulsion from +the project and project-sponsored spaces. We will notify the accused of the +report and provide them an opportunity to discuss it before any action is taken. +The identity of the reporter will be omitted from the details of the report +supplied to the accused. In potentially harmful situations, such as ongoing +harassment or threats to anyone's safety, we may take action without notice. + +## Attribution + +This Code of Conduct is adapted from the Contributor Covenant, version 1.4, +available at +https://www.contributor-covenant.org/version/1/4/code-of-conduct.html \ No newline at end of file diff --git a/synth.metadata b/synth.metadata index 29c86815..032c37f8 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "da5c6050d13b4950c82666a81d8acd25157664ae" + "sha": "5f6ef0ec5501d33c4667885b37a7685a30d41a76" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "da5c6050d13b4950c82666a81d8acd25157664ae" + "sha": "5f6ef0ec5501d33c4667885b37a7685a30d41a76" } } ], From 618104befbf52c1421d8ddd654bad7dd427b398c Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:13:36 -0700 Subject: [PATCH 14/47] docs: add proto-plus to intersphinx mapping Source-Author: Tim Swast Source-Date: Tue Oct 27 12:01:14 2020 -0500 Source-Repo: googleapis/synthtool Source-Sha: ea52b8a0bd560f72f376efcf45197fb7c8869120 Source-Link: https://github.com/googleapis/synthtool/commit/ea52b8a0bd560f72f376efcf45197fb7c8869120 --- docs/conf.py | 1 + synth.metadata | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 58f6dd24..a8c3aeeb 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -349,6 +349,7 @@ "google-auth": ("https://google-auth.readthedocs.io/en/stable", None), "google.api_core": ("https://googleapis.dev/python/google-api-core/latest/", None,), "grpc": ("https://grpc.io/grpc/python/", None), + "proto-plus": ("https://proto-plus-python.readthedocs.io/en/latest/", None), } diff --git a/synth.metadata b/synth.metadata index 032c37f8..66ace538 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "5f6ef0ec5501d33c4667885b37a7685a30d41a76" + "sha": "ea52b8a0bd560f72f376efcf45197fb7c8869120" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "5f6ef0ec5501d33c4667885b37a7685a30d41a76" + "sha": "ea52b8a0bd560f72f376efcf45197fb7c8869120" } } ], From d2e510c9fe6638647633cbecb244f9657df33ca2 Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:13:36 -0700 Subject: [PATCH 15/47] fix(python_library): fix external unit test dependencies I recently submitted https://github.com/googleapis/synthtool/pull/811/files, allowing external dependencies for unit tests. This fixes a small missing comma bug Source-Author: Daniel Sanche Source-Date: Thu Oct 29 16:58:01 2020 -0700 Source-Repo: googleapis/synthtool Source-Sha: 6542bd723403513626f61642fc02ddca528409aa Source-Link: https://github.com/googleapis/synthtool/commit/6542bd723403513626f61642fc02ddca528409aa --- noxfile.py | 4 +++- synth.metadata | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/noxfile.py b/noxfile.py index 1b2bd247..6a436ff2 100644 --- a/noxfile.py +++ b/noxfile.py @@ -72,7 +72,9 @@ def default(session): # Install all test dependencies, then install this package in-place. session.install("asyncmock", "pytest-asyncio") - session.install("mock", "pytest", "pytest-cov") + session.install( + "mock", "pytest", "pytest-cov", + ) session.install("-e", ".") # Run py.test against the unit tests. diff --git a/synth.metadata b/synth.metadata index 66ace538..65c1d80a 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "ea52b8a0bd560f72f376efcf45197fb7c8869120" + "sha": "6542bd723403513626f61642fc02ddca528409aa" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "ea52b8a0bd560f72f376efcf45197fb7c8869120" + "sha": "6542bd723403513626f61642fc02ddca528409aa" } } ], From e62227d2fe561ab35407806301b13a7819a24c94 Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:13:59 -0700 Subject: [PATCH 16/47] chore: add type hint check Source-Author: Leah E. Cole <6719667+leahecole@users.noreply.github.com> Source-Date: Wed Nov 4 17:36:32 2020 -0800 Source-Repo: googleapis/synthtool Source-Sha: 3d3e94c4e02370f307a9a200b0c743c3d8d19f29 Source-Link: https://github.com/googleapis/synthtool/commit/3d3e94c4e02370f307a9a200b0c743c3d8d19f29 --- samples/snippets/noxfile.py | 9 ++++++++- synth.metadata | 4 ++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/samples/snippets/noxfile.py b/samples/snippets/noxfile.py index 01686e4a..4621115e 100644 --- a/samples/snippets/noxfile.py +++ b/samples/snippets/noxfile.py @@ -39,6 +39,10 @@ # You can opt out from the test for specific Python versions. 'ignored_versions': ["2.7"], + # Old samples are opted out of enforcing Python type hints + # All new samples should feature them + 'enforce_type_hints': False, + # An envvar key for determining the project id to use. Change it # to 'BUILD_SPECIFIC_GCLOUD_PROJECT' if you want to opt in using a # build specific Cloud project. You can also use your own string @@ -132,7 +136,10 @@ def _determine_local_import_names(start_dir): @nox.session def lint(session): - session.install("flake8", "flake8-import-order") + if not TEST_CONFIG['enforce_type_hints']: + session.install("flake8", "flake8-import-order") + else: + session.install("flake8", "flake8-import-order", "flake8-annotations") local_names = _determine_local_import_names(".") args = FLAKE8_COMMON_ARGS + [ diff --git a/synth.metadata b/synth.metadata index 65c1d80a..45424ff6 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "6542bd723403513626f61642fc02ddca528409aa" + "sha": "3d3e94c4e02370f307a9a200b0c743c3d8d19f29" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "6542bd723403513626f61642fc02ddca528409aa" + "sha": "3d3e94c4e02370f307a9a200b0c743c3d8d19f29" } } ], From 9e099b5dd10c9f893f3fa380adb7b449586eda48 Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:14:10 -0700 Subject: [PATCH 17/47] chore: add blacken to template Source-Author: Leah E. Cole <6719667+leahecole@users.noreply.github.com> Source-Date: Thu Nov 5 15:22:03 2020 -0800 Source-Repo: googleapis/synthtool Source-Sha: 1f1148d3c7a7a52f0c98077f976bd9b3c948ee2b Source-Link: https://github.com/googleapis/synthtool/commit/1f1148d3c7a7a52f0c98077f976bd9b3c948ee2b --- samples/snippets/noxfile.py | 9 +++++++++ synth.metadata | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/samples/snippets/noxfile.py b/samples/snippets/noxfile.py index 4621115e..17300976 100644 --- a/samples/snippets/noxfile.py +++ b/samples/snippets/noxfile.py @@ -148,7 +148,16 @@ def lint(session): "." ] session.run("flake8", *args) +# +# Black +# + +@nox.session +def blacken(session): + session.install("black") + python_files = [path for path in os.listdir(".") if path.endswith(".py")] + session.run("black", *python_files) # # Sample Tests diff --git a/synth.metadata b/synth.metadata index 45424ff6..d4fca404 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "3d3e94c4e02370f307a9a200b0c743c3d8d19f29" + "sha": "1f1148d3c7a7a52f0c98077f976bd9b3c948ee2b" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "3d3e94c4e02370f307a9a200b0c743c3d8d19f29" + "sha": "1f1148d3c7a7a52f0c98077f976bd9b3c948ee2b" } } ], From 44f90a1ba841458031a3e084db854fd96beb825e Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:14:57 -0700 Subject: [PATCH 18/47] fix: address lint issues Source-Author: Leah E. Cole <6719667+leahecole@users.noreply.github.com> Source-Date: Thu Nov 12 11:30:49 2020 -0800 Source-Repo: googleapis/synthtool Source-Sha: e89175cf074dccc4babb4eca66ae913696e47a71 Source-Link: https://github.com/googleapis/synthtool/commit/e89175cf074dccc4babb4eca66ae913696e47a71 --- samples/snippets/noxfile.py | 1 + synth.metadata | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/samples/snippets/noxfile.py b/samples/snippets/noxfile.py index 17300976..b90eef00 100644 --- a/samples/snippets/noxfile.py +++ b/samples/snippets/noxfile.py @@ -152,6 +152,7 @@ def lint(session): # Black # + @nox.session def blacken(session): session.install("black") diff --git a/synth.metadata b/synth.metadata index d4fca404..a5b1b412 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "1f1148d3c7a7a52f0c98077f976bd9b3c948ee2b" + "sha": "e89175cf074dccc4babb4eca66ae913696e47a71" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "1f1148d3c7a7a52f0c98077f976bd9b3c948ee2b" + "sha": "e89175cf074dccc4babb4eca66ae913696e47a71" } } ], From accfb41dcbd2aa319fa17ab1505c5e4704fc72f7 Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:15:20 -0700 Subject: [PATCH 19/47] docs(python): update intersphinx for grpc and auth * docs(python): update intersphinx for grpc and auth * use https for python intersphinx Co-authored-by: Tim Swast Source-Author: Bu Sun Kim <8822365+busunkim96@users.noreply.github.com> Source-Date: Wed Nov 18 14:37:25 2020 -0700 Source-Repo: googleapis/synthtool Source-Sha: 9a7d9fbb7045c34c9d3d22c1ff766eeae51f04c9 Source-Link: https://github.com/googleapis/synthtool/commit/9a7d9fbb7045c34c9d3d22c1ff766eeae51f04c9 --- docs/conf.py | 9 ++++++--- synth.metadata | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index a8c3aeeb..6d62a1f4 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -345,10 +345,13 @@ # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = { - "python": ("http://python.readthedocs.org/en/latest/", None), - "google-auth": ("https://google-auth.readthedocs.io/en/stable", None), + "python": ("https://python.readthedocs.org/en/latest/", None), + "google-auth": ( + "https://googleapis.dev/python/google-auth/latest/index.html", + None, + ), "google.api_core": ("https://googleapis.dev/python/google-api-core/latest/", None,), - "grpc": ("https://grpc.io/grpc/python/", None), + "grpc": ("https://grpc.github.io/grpc/python/", None), "proto-plus": ("https://proto-plus-python.readthedocs.io/en/latest/", None), } diff --git a/synth.metadata b/synth.metadata index a5b1b412..dd4b38da 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "e89175cf074dccc4babb4eca66ae913696e47a71" + "sha": "9a7d9fbb7045c34c9d3d22c1ff766eeae51f04c9" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "e89175cf074dccc4babb4eca66ae913696e47a71" + "sha": "9a7d9fbb7045c34c9d3d22c1ff766eeae51f04c9" } } ], From 55ca0d20e8186a09f41fb7b30d57bdf0c9f3dff9 Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:15:31 -0700 Subject: [PATCH 20/47] docs(python): fix intersphinx link for google-auth Source-Author: Bu Sun Kim <8822365+busunkim96@users.noreply.github.com> Source-Date: Thu Nov 19 10:16:05 2020 -0700 Source-Repo: googleapis/synthtool Source-Sha: a073c873f3928c561bdf87fdfbf1d081d1998984 Source-Link: https://github.com/googleapis/synthtool/commit/a073c873f3928c561bdf87fdfbf1d081d1998984 --- docs/conf.py | 5 +---- synth.metadata | 4 ++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 6d62a1f4..9f3c1dd4 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -346,10 +346,7 @@ # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = { "python": ("https://python.readthedocs.org/en/latest/", None), - "google-auth": ( - "https://googleapis.dev/python/google-auth/latest/index.html", - None, - ), + "google-auth": ("https://googleapis.dev/python/google-auth/latest/", None), "google.api_core": ("https://googleapis.dev/python/google-api-core/latest/", None,), "grpc": ("https://grpc.github.io/grpc/python/", None), "proto-plus": ("https://proto-plus-python.readthedocs.io/en/latest/", None), diff --git a/synth.metadata b/synth.metadata index dd4b38da..ef06b941 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "9a7d9fbb7045c34c9d3d22c1ff766eeae51f04c9" + "sha": "a073c873f3928c561bdf87fdfbf1d081d1998984" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "9a7d9fbb7045c34c9d3d22c1ff766eeae51f04c9" + "sha": "a073c873f3928c561bdf87fdfbf1d081d1998984" } } ], From 8532c6601f5492f6b67c269f2679e44ae06119c8 Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:16:41 -0700 Subject: [PATCH 21/47] chore: add config / docs for 'pre-commit' support Source-Author: Tres Seaver Source-Date: Tue Dec 1 16:01:20 2020 -0500 Source-Repo: googleapis/synthtool Source-Sha: 32af6da519a6b042e3da62008e2a75e991efb6b4 Source-Link: https://github.com/googleapis/synthtool/commit/32af6da519a6b042e3da62008e2a75e991efb6b4 --- .pre-commit-config.yaml | 17 +++++++++++++++++ CONTRIBUTING.rst | 10 ++++++++++ synth.metadata | 5 +++-- 3 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..fb80a95e --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,17 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v3.2.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml +- repo: https://github.com/psf/black + rev: 19.10b0 + hooks: + - id: black +- repo: https://gitlab.com/pycqa/flake8 + rev: 3.8.4 + hooks: + - id: flake8 diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index d6c460d6..541d314c 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -111,6 +111,16 @@ Coding Style should point to the official ``googleapis`` checkout and the the branch should be the main branch on that remote (``master``). +- This repository contains configuration for the + `pre-commit `__ tool, which automates checking + our linters during a commit. If you have it installed on your ``$PATH``, + you can enable enforcing those checks via: + +.. code-block:: bash + + $ pre-commit install + pre-commit installed at .git/hooks/pre-commit + Exceptions to PEP8: - Many unit tests use a helper method, ``_call_fut`` ("FUT" is short for diff --git a/synth.metadata b/synth.metadata index ef06b941..038e7aaa 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "a073c873f3928c561bdf87fdfbf1d081d1998984" + "sha": "32af6da519a6b042e3da62008e2a75e991efb6b4" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "a073c873f3928c561bdf87fdfbf1d081d1998984" + "sha": "32af6da519a6b042e3da62008e2a75e991efb6b4" } } ], @@ -95,6 +95,7 @@ ".kokoro/test-samples.sh", ".kokoro/trampoline.sh", ".kokoro/trampoline_v2.sh", + ".pre-commit-config.yaml", ".trampolinerc", "CODE_OF_CONDUCT.md", "CONTRIBUTING.rst", From d5aa2a42dcdcfeffea30a8a93b3cf59fca2f0adf Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:16:53 -0700 Subject: [PATCH 22/47] chore(deps): update precommit hook pre-commit/pre-commit-hooks to v3.3.0 Source-Author: WhiteSource Renovate Source-Date: Wed Dec 2 17:18:24 2020 +0100 Source-Repo: googleapis/synthtool Source-Sha: 69629b64b83c6421d616be2b8e11795738ec8a6c Source-Link: https://github.com/googleapis/synthtool/commit/69629b64b83c6421d616be2b8e11795738ec8a6c --- .pre-commit-config.yaml | 2 +- synth.metadata | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fb80a95e..6ad83346 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ # See https://pre-commit.com/hooks.html for more hooks repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v3.2.0 + rev: v3.3.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer diff --git a/synth.metadata b/synth.metadata index 038e7aaa..2eb8eff5 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "32af6da519a6b042e3da62008e2a75e991efb6b4" + "sha": "69629b64b83c6421d616be2b8e11795738ec8a6c" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "32af6da519a6b042e3da62008e2a75e991efb6b4" + "sha": "69629b64b83c6421d616be2b8e11795738ec8a6c" } } ], From 478b353a263e5e4cadbb7d351feaf6e6fda10276 Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:16:53 -0700 Subject: [PATCH 23/47] test(python): give filesystem paths to pytest-cov https://pytest-cov.readthedocs.io/en/latest/config.html The pytest-cov docs seem to suggest a filesystem path is expected. Source-Author: Bu Sun Kim <8822365+busunkim96@users.noreply.github.com> Source-Date: Wed Dec 2 09:28:04 2020 -0700 Source-Repo: googleapis/synthtool Source-Sha: f94318521f63085b9ccb43d42af89f153fb39f15 Source-Link: https://github.com/googleapis/synthtool/commit/f94318521f63085b9ccb43d42af89f153fb39f15 --- noxfile.py | 5 ++--- synth.metadata | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/noxfile.py b/noxfile.py index 6a436ff2..8004482e 100644 --- a/noxfile.py +++ b/noxfile.py @@ -81,9 +81,8 @@ def default(session): session.run( "py.test", "--quiet", - "--cov=google.cloud.cloudtrace", - "--cov=google.cloud", - "--cov=tests.unit", + "--cov=google/cloud", + "--cov=tests/unit", "--cov-append", "--cov-config=.coveragerc", "--cov-report=", diff --git a/synth.metadata b/synth.metadata index 2eb8eff5..54814616 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "69629b64b83c6421d616be2b8e11795738ec8a6c" + "sha": "f94318521f63085b9ccb43d42af89f153fb39f15" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "69629b64b83c6421d616be2b8e11795738ec8a6c" + "sha": "f94318521f63085b9ccb43d42af89f153fb39f15" } } ], From e80d3b80d0b0a9291ef5661a3b44bcab92d9bed2 Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:17:16 -0700 Subject: [PATCH 24/47] chore: update noxfile.py.j2 * Update noxfile.py.j2 add changes from @glasnt to the template template to ensure that enforcing type hinting doesn't fail for repos with the sample noxfile (aka all samples repos) See https://github.com/GoogleCloudPlatform/python-docs-samples/pull/4869/files for context * fix typo Source-Author: Leah E. Cole <6719667+leahecole@users.noreply.github.com> Source-Date: Thu Dec 3 13:44:30 2020 -0800 Source-Repo: googleapis/synthtool Source-Sha: 18c5dbdb4ac8cf75d4d8174e7b4558f48e76f8a1 Source-Link: https://github.com/googleapis/synthtool/commit/18c5dbdb4ac8cf75d4d8174e7b4558f48e76f8a1 --- samples/snippets/noxfile.py | 17 +++++++++-------- synth.metadata | 4 ++-- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/samples/snippets/noxfile.py b/samples/snippets/noxfile.py index b90eef00..bca0522e 100644 --- a/samples/snippets/noxfile.py +++ b/samples/snippets/noxfile.py @@ -17,6 +17,7 @@ import os from pathlib import Path import sys +from typing import Callable, Dict, List, Optional import nox @@ -68,7 +69,7 @@ TEST_CONFIG.update(TEST_CONFIG_OVERRIDE) -def get_pytest_env_vars(): +def get_pytest_env_vars() -> Dict[str, str]: """Returns a dict for pytest invocation.""" ret = {} @@ -97,7 +98,7 @@ def get_pytest_env_vars(): # -def _determine_local_import_names(start_dir): +def _determine_local_import_names(start_dir: str) -> List[str]: """Determines all import names that should be considered "local". This is used when running the linter to insure that import order is @@ -135,7 +136,7 @@ def _determine_local_import_names(start_dir): @nox.session -def lint(session): +def lint(session: nox.sessions.Session) -> None: if not TEST_CONFIG['enforce_type_hints']: session.install("flake8", "flake8-import-order") else: @@ -154,7 +155,7 @@ def lint(session): @nox.session -def blacken(session): +def blacken(session: nox.sessions.Session) -> None: session.install("black") python_files = [path for path in os.listdir(".") if path.endswith(".py")] @@ -168,7 +169,7 @@ def blacken(session): PYTEST_COMMON_ARGS = ["--junitxml=sponge_log.xml"] -def _session_tests(session, post_install=None): +def _session_tests(session: nox.sessions.Session, post_install: Callable = None) -> None: """Runs py.test for a particular project.""" if os.path.exists("requirements.txt"): session.install("-r", "requirements.txt") @@ -194,7 +195,7 @@ def _session_tests(session, post_install=None): @nox.session(python=ALL_VERSIONS) -def py(session): +def py(session: nox.sessions.Session) -> None: """Runs py.test for a sample using the specified version of Python.""" if session.python in TESTED_VERSIONS: _session_tests(session) @@ -209,7 +210,7 @@ def py(session): # -def _get_repo_root(): +def _get_repo_root() -> Optional[str]: """ Returns the root folder of the project. """ # Get root of this repository. Assume we don't have directories nested deeper than 10 items. p = Path(os.getcwd()) @@ -232,7 +233,7 @@ def _get_repo_root(): @nox.session @nox.parametrize("path", GENERATED_READMES) -def readmegen(session, path): +def readmegen(session: nox.sessions.Session, path: str) -> None: """(Re-)generates the readme for a sample.""" session.install("jinja2", "pyyaml") dir_ = os.path.dirname(path) diff --git a/synth.metadata b/synth.metadata index 54814616..c0c9a676 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "f94318521f63085b9ccb43d42af89f153fb39f15" + "sha": "18c5dbdb4ac8cf75d4d8174e7b4558f48e76f8a1" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "f94318521f63085b9ccb43d42af89f153fb39f15" + "sha": "18c5dbdb4ac8cf75d4d8174e7b4558f48e76f8a1" } } ], From dcd2d41855b7975892bda58a27c69f583e020086 Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:17:52 -0700 Subject: [PATCH 25/47] chore(deps): update precommit hook pre-commit/pre-commit-hooks to v3.4.0 Co-authored-by: Tres Seaver Source-Author: WhiteSource Renovate Source-Date: Wed Dec 16 18:13:24 2020 +0100 Source-Repo: googleapis/synthtool Source-Sha: aa255b15d52b6d8950cca48cfdf58f7d27a60c8a Source-Link: https://github.com/googleapis/synthtool/commit/aa255b15d52b6d8950cca48cfdf58f7d27a60c8a --- .pre-commit-config.yaml | 2 +- synth.metadata | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6ad83346..a9024b15 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ # See https://pre-commit.com/hooks.html for more hooks repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v3.3.0 + rev: v3.4.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer diff --git a/synth.metadata b/synth.metadata index c0c9a676..294dccc3 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "18c5dbdb4ac8cf75d4d8174e7b4558f48e76f8a1" + "sha": "aa255b15d52b6d8950cca48cfdf58f7d27a60c8a" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "18c5dbdb4ac8cf75d4d8174e7b4558f48e76f8a1" + "sha": "aa255b15d52b6d8950cca48cfdf58f7d27a60c8a" } } ], From 33d71587d0e75a3da1f4cd2c795afbe35a658a98 Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:18:16 -0700 Subject: [PATCH 26/47] docs(python): document adding Python 3.9 support, dropping 3.5 support Closes #787 Source-Author: Tres Seaver Source-Date: Thu Dec 17 16:08:02 2020 -0500 Source-Repo: googleapis/synthtool Source-Sha: b670a77a454f415d247907908e8ee7943e06d718 Source-Link: https://github.com/googleapis/synthtool/commit/b670a77a454f415d247907908e8ee7943e06d718 --- CONTRIBUTING.rst | 11 +++++------ synth.metadata | 4 ++-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 541d314c..ab65833f 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -21,8 +21,8 @@ In order to add a feature: - The feature must be documented in both the API and narrative documentation. -- The feature must work fully on the following CPython versions: 2.7, - 3.5, 3.6, 3.7 and 3.8 on both UNIX and Windows. +- The feature must work fully on the following CPython versions: + 3.6, 3.7, 3.8 and 3.9 on both UNIX and Windows. - The feature must not add unnecessary dependencies (where "unnecessary" is of course subjective, but new dependencies should @@ -202,25 +202,24 @@ Supported Python Versions We support: -- `Python 3.5`_ - `Python 3.6`_ - `Python 3.7`_ - `Python 3.8`_ +- `Python 3.9`_ -.. _Python 3.5: https://docs.python.org/3.5/ .. _Python 3.6: https://docs.python.org/3.6/ .. _Python 3.7: https://docs.python.org/3.7/ .. _Python 3.8: https://docs.python.org/3.8/ +.. _Python 3.9: https://docs.python.org/3.9/ Supported versions can be found in our ``noxfile.py`` `config`_. .. _config: https://github.com/googleapis/python-trace/blob/master/noxfile.py -Python 2.7 support is deprecated. All code changes should maintain Python 2.7 compatibility until January 1, 2020. We also explicitly decided to support Python 3 beginning with version -3.5. Reasons for this include: +3.6. Reasons for this include: - Encouraging use of newest versions of Python 3 - Taking the lead of `prominent`_ open-source `projects`_ diff --git a/synth.metadata b/synth.metadata index 294dccc3..123ca102 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "aa255b15d52b6d8950cca48cfdf58f7d27a60c8a" + "sha": "b670a77a454f415d247907908e8ee7943e06d718" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "aa255b15d52b6d8950cca48cfdf58f7d27a60c8a" + "sha": "b670a77a454f415d247907908e8ee7943e06d718" } } ], From c479ada9ff992eda07bc627a9a2a0965c9ee64b5 Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:18:51 -0700 Subject: [PATCH 27/47] chore: exclude `.nox` directories from linting The samples tests create `.nox` directories with all dependencies installed. These directories should be excluded from linting. I've tested this change locally, and it significantly speeds up linting on my machine. Source-Author: Tim Swast Source-Date: Tue Dec 22 13:04:04 2020 -0600 Source-Repo: googleapis/synthtool Source-Sha: 373861061648b5fe5e0ac4f8a38b32d639ee93e4 Source-Link: https://github.com/googleapis/synthtool/commit/373861061648b5fe5e0ac4f8a38b32d639ee93e4 --- .flake8 | 1 + synth.metadata | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.flake8 b/.flake8 index ed931638..29227d4c 100644 --- a/.flake8 +++ b/.flake8 @@ -26,6 +26,7 @@ exclude = *_pb2.py # Standard linting exemptions. + **/.nox/** __pycache__, .git, *.pyc, diff --git a/synth.metadata b/synth.metadata index 123ca102..e74ba0da 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "b670a77a454f415d247907908e8ee7943e06d718" + "sha": "373861061648b5fe5e0ac4f8a38b32d639ee93e4" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "b670a77a454f415d247907908e8ee7943e06d718" + "sha": "373861061648b5fe5e0ac4f8a38b32d639ee93e4" } } ], From 6ecbf333566439ada3d37e776df872b8009cd6ca Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:19:52 -0700 Subject: [PATCH 28/47] chore(python): fix column sizing issue in docs Source-Author: Bu Sun Kim <8822365+busunkim96@users.noreply.github.com> Source-Date: Thu Jan 7 11:58:32 2021 -0700 Source-Repo: googleapis/synthtool Source-Sha: f15b57ccfd71106c2299e9b89835fe6e55015662 Source-Link: https://github.com/googleapis/synthtool/commit/f15b57ccfd71106c2299e9b89835fe6e55015662 --- docs/_static/custom.css | 7 ++++++- synth.metadata | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/_static/custom.css b/docs/_static/custom.css index 0abaf229..bcd37bbd 100644 --- a/docs/_static/custom.css +++ b/docs/_static/custom.css @@ -1,4 +1,9 @@ div#python2-eol { border-color: red; border-width: medium; -} \ No newline at end of file +} + +/* Ensure minimum width for 'Parameters' / 'Returns' column */ +dl.field-list > dt { + min-width: 100px +} diff --git a/synth.metadata b/synth.metadata index e74ba0da..c79ed1ca 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "373861061648b5fe5e0ac4f8a38b32d639ee93e4" + "sha": "f15b57ccfd71106c2299e9b89835fe6e55015662" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "373861061648b5fe5e0ac4f8a38b32d639ee93e4" + "sha": "f15b57ccfd71106c2299e9b89835fe6e55015662" } } ], From 6d3dedad713ff421fb0bb58da91a5f4a09e5e313 Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:19:52 -0700 Subject: [PATCH 29/47] chore(python): use 'http' in LICENSE Co-authored-by: Tim Swast Source-Author: Bu Sun Kim <8822365+busunkim96@users.noreply.github.com> Source-Date: Thu Jan 7 13:05:12 2021 -0700 Source-Repo: googleapis/synthtool Source-Sha: 41a4e56982620d3edcf110d76f4fcdfdec471ac8 Source-Link: https://github.com/googleapis/synthtool/commit/41a4e56982620d3edcf110d76f4fcdfdec471ac8 --- LICENSE | 7 ++++--- synth.metadata | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/LICENSE b/LICENSE index a8ee855d..d6456956 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ - Apache License + + Apache License Version 2.0, January 2004 - https://www.apache.org/licenses/ + http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION @@ -192,7 +193,7 @@ you may not use this file except in compliance with the License. You may obtain a copy of the License at - https://www.apache.org/licenses/LICENSE-2.0 + 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, diff --git a/synth.metadata b/synth.metadata index c79ed1ca..ac090eca 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "f15b57ccfd71106c2299e9b89835fe6e55015662" + "sha": "41a4e56982620d3edcf110d76f4fcdfdec471ac8" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "f15b57ccfd71106c2299e9b89835fe6e55015662" + "sha": "41a4e56982620d3edcf110d76f4fcdfdec471ac8" } } ], From 19ea754a35f22136699e0b3552052517e3c9bc7c Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:20:27 -0700 Subject: [PATCH 30/47] chore(python): skip docfx in main presubmit * chore(python): skip docfx in main presubmit * fix: properly template the repo name Source-Author: Bu Sun Kim <8822365+busunkim96@users.noreply.github.com> Source-Date: Fri Jan 8 10:32:13 2021 -0700 Source-Repo: googleapis/synthtool Source-Sha: fb53b6fb373b7c3edf4e55f3e8036bc6d73fa483 Source-Link: https://github.com/googleapis/synthtool/commit/fb53b6fb373b7c3edf4e55f3e8036bc6d73fa483 --- .kokoro/build.sh | 16 ++++++++++------ .kokoro/docs/docs-presubmit.cfg | 11 +++++++++++ .trampolinerc | 2 ++ noxfile.py | 11 +++++++++++ synth.metadata | 4 ++-- 5 files changed, 36 insertions(+), 8 deletions(-) diff --git a/.kokoro/build.sh b/.kokoro/build.sh index 40ae9af9..84709de2 100755 --- a/.kokoro/build.sh +++ b/.kokoro/build.sh @@ -15,7 +15,11 @@ set -eo pipefail -cd github/python-trace +if [[ -z "${PROJECT_ROOT:-}" ]]; then + PROJECT_ROOT="github/python-trace" +fi + +cd "${PROJECT_ROOT}" # Disable buffering, so that the logs stream through. export PYTHONUNBUFFERED=1 @@ -30,16 +34,16 @@ export GOOGLE_APPLICATION_CREDENTIALS=${KOKORO_GFILE_DIR}/service-account.json export PROJECT_ID=$(cat "${KOKORO_GFILE_DIR}/project-id.json") # Remove old nox -python3.6 -m pip uninstall --yes --quiet nox-automation +python3 -m pip uninstall --yes --quiet nox-automation # Install nox -python3.6 -m pip install --upgrade --quiet nox -python3.6 -m nox --version +python3 -m pip install --upgrade --quiet nox +python3 -m nox --version # If NOX_SESSION is set, it only runs the specified session, # otherwise run all the sessions. if [[ -n "${NOX_SESSION:-}" ]]; then - python3.6 -m nox -s "${NOX_SESSION:-}" + python3 -m nox -s ${NOX_SESSION:-} else - python3.6 -m nox + python3 -m nox fi diff --git a/.kokoro/docs/docs-presubmit.cfg b/.kokoro/docs/docs-presubmit.cfg index 11181078..5095db91 100644 --- a/.kokoro/docs/docs-presubmit.cfg +++ b/.kokoro/docs/docs-presubmit.cfg @@ -15,3 +15,14 @@ env_vars: { key: "TRAMPOLINE_IMAGE_UPLOAD" value: "false" } + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: github/python-trace/.kokoro/build.sh" +} + +# Only run this nox session. +env_vars: { + key: "NOX_SESSION" + value: "docs docfx" +} diff --git a/.trampolinerc b/.trampolinerc index 995ee291..c7d663ae 100644 --- a/.trampolinerc +++ b/.trampolinerc @@ -18,12 +18,14 @@ required_envvars+=( "STAGING_BUCKET" "V2_STAGING_BUCKET" + "NOX_SESSION" ) # Add env vars which are passed down into the container here. pass_down_envvars+=( "STAGING_BUCKET" "V2_STAGING_BUCKET" + "NOX_SESSION" ) # Prevent unintentional override on the default image. diff --git a/noxfile.py b/noxfile.py index 8004482e..e938b7f3 100644 --- a/noxfile.py +++ b/noxfile.py @@ -30,6 +30,17 @@ SYSTEM_TEST_PYTHON_VERSIONS = ["3.8"] UNIT_TEST_PYTHON_VERSIONS = ["3.6", "3.7", "3.8", "3.9"] +# 'docfx' is excluded since it only needs to run in 'docs-presubmit' +nox.options.sessions = [ + "unit", + "system", + "cover", + "lint", + "lint_setup_py", + "blacken", + "docs", +] + @nox.session(python=DEFAULT_PYTHON_VERSION) def lint(session): diff --git a/synth.metadata b/synth.metadata index ac090eca..583ce980 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "41a4e56982620d3edcf110d76f4fcdfdec471ac8" + "sha": "fb53b6fb373b7c3edf4e55f3e8036bc6d73fa483" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "41a4e56982620d3edcf110d76f4fcdfdec471ac8" + "sha": "fb53b6fb373b7c3edf4e55f3e8036bc6d73fa483" } } ], From 8d75f79370037fac4f53a1c4ad0f90c6629e3ee7 Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:20:28 -0700 Subject: [PATCH 31/47] chore: add missing quotation mark Source-Author: Bu Sun Kim <8822365+busunkim96@users.noreply.github.com> Source-Date: Mon Jan 11 09:43:06 2021 -0700 Source-Repo: googleapis/synthtool Source-Sha: 16ec872dd898d7de6e1822badfac32484b5d9031 Source-Link: https://github.com/googleapis/synthtool/commit/16ec872dd898d7de6e1822badfac32484b5d9031 --- .kokoro/docs/docs-presubmit.cfg | 2 +- synth.metadata | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.kokoro/docs/docs-presubmit.cfg b/.kokoro/docs/docs-presubmit.cfg index 5095db91..77b74e00 100644 --- a/.kokoro/docs/docs-presubmit.cfg +++ b/.kokoro/docs/docs-presubmit.cfg @@ -18,7 +18,7 @@ env_vars: { env_vars: { key: "TRAMPOLINE_BUILD_FILE" - value: github/python-trace/.kokoro/build.sh" + value: "github/python-trace/.kokoro/build.sh" } # Only run this nox session. diff --git a/synth.metadata b/synth.metadata index 583ce980..ad43d267 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "fb53b6fb373b7c3edf4e55f3e8036bc6d73fa483" + "sha": "16ec872dd898d7de6e1822badfac32484b5d9031" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "fb53b6fb373b7c3edf4e55f3e8036bc6d73fa483" + "sha": "16ec872dd898d7de6e1822badfac32484b5d9031" } } ], From b3a737800d3b3a500b6b715e10d2300fd8aa23ee Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:20:39 -0700 Subject: [PATCH 32/47] chore: add 3.9 to noxfile template Since the python-docs-samples noxfile-template doesn't sync with this, I wanted to make sure the noxfile template matched the most recent change [here](https://github.com/GoogleCloudPlatform/python-docs-samples/pull/4968/files) cc @tmatsuo Source-Author: Leah E. Cole <6719667+leahecole@users.noreply.github.com> Source-Date: Fri Jan 15 17:24:05 2021 -0800 Source-Repo: googleapis/synthtool Source-Sha: 56ddc68f36b32341e9f22c2c59b4ce6aa3ba635f Source-Link: https://github.com/googleapis/synthtool/commit/56ddc68f36b32341e9f22c2c59b4ce6aa3ba635f --- samples/snippets/noxfile.py | 2 +- synth.metadata | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/samples/snippets/noxfile.py b/samples/snippets/noxfile.py index bca0522e..97bf7da8 100644 --- a/samples/snippets/noxfile.py +++ b/samples/snippets/noxfile.py @@ -85,7 +85,7 @@ def get_pytest_env_vars() -> Dict[str, str]: # DO NOT EDIT - automatically generated. # All versions used to tested samples. -ALL_VERSIONS = ["2.7", "3.6", "3.7", "3.8"] +ALL_VERSIONS = ["2.7", "3.6", "3.7", "3.8", "3.9"] # Any default versions that should be ignored. IGNORED_VERSIONS = TEST_CONFIG['ignored_versions'] diff --git a/synth.metadata b/synth.metadata index ad43d267..9647493f 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "16ec872dd898d7de6e1822badfac32484b5d9031" + "sha": "56ddc68f36b32341e9f22c2c59b4ce6aa3ba635f" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "16ec872dd898d7de6e1822badfac32484b5d9031" + "sha": "56ddc68f36b32341e9f22c2c59b4ce6aa3ba635f" } } ], From 22099495642e96fdf26e391bed5387beb4ab78da Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:20:51 -0700 Subject: [PATCH 33/47] build(python): make `NOX_SESSION` optional I added this accidentally in #889. `NOX_SESSION` should be passed down if it is set but not marked required. Source-Author: Bu Sun Kim <8822365+busunkim96@users.noreply.github.com> Source-Date: Tue Jan 19 09:38:04 2021 -0700 Source-Repo: googleapis/synthtool Source-Sha: ba960d730416fe05c50547e975ce79fcee52c671 Source-Link: https://github.com/googleapis/synthtool/commit/ba960d730416fe05c50547e975ce79fcee52c671 --- .trampolinerc | 1 - synth.metadata | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.trampolinerc b/.trampolinerc index c7d663ae..383b6ec8 100644 --- a/.trampolinerc +++ b/.trampolinerc @@ -18,7 +18,6 @@ required_envvars+=( "STAGING_BUCKET" "V2_STAGING_BUCKET" - "NOX_SESSION" ) # Add env vars which are passed down into the container here. diff --git a/synth.metadata b/synth.metadata index 9647493f..3e14f2b5 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "56ddc68f36b32341e9f22c2c59b4ce6aa3ba635f" + "sha": "ba960d730416fe05c50547e975ce79fcee52c671" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "56ddc68f36b32341e9f22c2c59b4ce6aa3ba635f" + "sha": "ba960d730416fe05c50547e975ce79fcee52c671" } } ], From b11b7b3766703cf1abf8b33d04994ce139f8d90b Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:21:26 -0700 Subject: [PATCH 34/47] chore: Add header checker config to python library synth MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that we have it working in [python-docs-samples](https://github.com/GoogleCloudPlatform/python-docs-samples/blob/master/.github/header-checker-lint.yml) we should consider adding it to the 🐍 libraries :) Source-Author: Leah E. Cole <6719667+leahecole@users.noreply.github.com> Source-Date: Mon Jan 25 13:24:08 2021 -0800 Source-Repo: googleapis/synthtool Source-Sha: 573f7655311b553a937f9123bee17bf78497db95 Source-Link: https://github.com/googleapis/synthtool/commit/573f7655311b553a937f9123bee17bf78497db95 --- .github/header-checker-lint.yml | 15 +++++++++++++++ synth.metadata | 5 +++-- 2 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 .github/header-checker-lint.yml diff --git a/.github/header-checker-lint.yml b/.github/header-checker-lint.yml new file mode 100644 index 00000000..fc281c05 --- /dev/null +++ b/.github/header-checker-lint.yml @@ -0,0 +1,15 @@ +{"allowedCopyrightHolders": ["Google LLC"], + "allowedLicenses": ["Apache-2.0", "MIT", "BSD-3"], + "ignoreFiles": ["**/requirements.txt", "**/requirements-test.txt"], + "sourceFileExtensions": [ + "ts", + "js", + "java", + "sh", + "Dockerfile", + "yaml", + "py", + "html", + "txt" + ] +} \ No newline at end of file diff --git a/synth.metadata b/synth.metadata index 3e14f2b5..53f320ac 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "ba960d730416fe05c50547e975ce79fcee52c671" + "sha": "573f7655311b553a937f9123bee17bf78497db95" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "ba960d730416fe05c50547e975ce79fcee52c671" + "sha": "573f7655311b553a937f9123bee17bf78497db95" } } ], @@ -58,6 +58,7 @@ ".github/ISSUE_TEMPLATE/feature_request.md", ".github/ISSUE_TEMPLATE/support_request.md", ".github/PULL_REQUEST_TEMPLATE.md", + ".github/header-checker-lint.yml", ".github/release-please.yml", ".github/snippet-bot.yml", ".gitignore", From 7fc3698e5ec531735d8dc28663f4f36a28c763fe Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:21:38 -0700 Subject: [PATCH 35/47] chore: add noxfile parameters for extra dependencies Also, add tests for some noxfile parameters for assurance that the template generates valid Python. Co-authored-by: Jeffrey Rennie Source-Author: Tim Swast Source-Date: Tue Jan 26 12:26:57 2021 -0600 Source-Repo: googleapis/synthtool Source-Sha: 778d8beae28d6d87eb01fdc839a4b4d966ed2ebe Source-Link: https://github.com/googleapis/synthtool/commit/778d8beae28d6d87eb01fdc839a4b4d966ed2ebe --- noxfile.py | 1 + synth.metadata | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/noxfile.py b/noxfile.py index e938b7f3..9e90799f 100644 --- a/noxfile.py +++ b/noxfile.py @@ -86,6 +86,7 @@ def default(session): session.install( "mock", "pytest", "pytest-cov", ) + session.install("-e", ".") # Run py.test against the unit tests. diff --git a/synth.metadata b/synth.metadata index 53f320ac..194ebd1f 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "573f7655311b553a937f9123bee17bf78497db95" + "sha": "778d8beae28d6d87eb01fdc839a4b4d966ed2ebe" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "573f7655311b553a937f9123bee17bf78497db95" + "sha": "778d8beae28d6d87eb01fdc839a4b4d966ed2ebe" } } ], From 76eeef14fef86de12cf094cdaf285d582a789ed9 Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:22:01 -0700 Subject: [PATCH 36/47] build: migrate to flakybot Source-Author: Justin Beckwith Source-Date: Thu Jan 28 22:22:38 2021 -0800 Source-Repo: googleapis/synthtool Source-Sha: d1bb9173100f62c0cfc8f3138b62241e7f47ca6a Source-Link: https://github.com/googleapis/synthtool/commit/d1bb9173100f62c0cfc8f3138b62241e7f47ca6a --- .kokoro/test-samples.sh | 8 ++++---- .kokoro/trampoline_v2.sh | 2 +- synth.metadata | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.kokoro/test-samples.sh b/.kokoro/test-samples.sh index 8ab944ec..2e42e1dd 100755 --- a/.kokoro/test-samples.sh +++ b/.kokoro/test-samples.sh @@ -87,11 +87,11 @@ for file in samples/**/requirements.txt; do python3.6 -m nox -s "$RUN_TESTS_SESSION" EXIT=$? - # If this is a periodic build, send the test log to the Build Cop Bot. - # See https://github.com/googleapis/repo-automation-bots/tree/master/packages/buildcop. + # If this is a periodic build, send the test log to the FlakyBot. + # See https://github.com/googleapis/repo-automation-bots/tree/master/packages/flakybot. if [[ $KOKORO_BUILD_ARTIFACTS_SUBDIR = *"periodic"* ]]; then - chmod +x $KOKORO_GFILE_DIR/linux_amd64/buildcop - $KOKORO_GFILE_DIR/linux_amd64/buildcop + chmod +x $KOKORO_GFILE_DIR/linux_amd64/flakybot + $KOKORO_GFILE_DIR/linux_amd64/flakybot fi if [[ $EXIT -ne 0 ]]; then diff --git a/.kokoro/trampoline_v2.sh b/.kokoro/trampoline_v2.sh index 719bcd5b..4af6cdc2 100755 --- a/.kokoro/trampoline_v2.sh +++ b/.kokoro/trampoline_v2.sh @@ -159,7 +159,7 @@ if [[ -n "${KOKORO_BUILD_ID:-}" ]]; then "KOKORO_GITHUB_COMMIT" "KOKORO_GITHUB_PULL_REQUEST_NUMBER" "KOKORO_GITHUB_PULL_REQUEST_COMMIT" - # For Build Cop Bot + # For FlakyBot "KOKORO_GITHUB_COMMIT_URL" "KOKORO_GITHUB_PULL_REQUEST_URL" ) diff --git a/synth.metadata b/synth.metadata index 194ebd1f..95a8d925 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "778d8beae28d6d87eb01fdc839a4b4d966ed2ebe" + "sha": "d1bb9173100f62c0cfc8f3138b62241e7f47ca6a" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "778d8beae28d6d87eb01fdc839a4b4d966ed2ebe" + "sha": "d1bb9173100f62c0cfc8f3138b62241e7f47ca6a" } } ], From 7f83c9157d76e8bce41b4fe1b55f2fa25cb4c955 Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:23:00 -0700 Subject: [PATCH 37/47] chore(python): include py.typed files in release A py.typed file must be included in the released package for it to be considered typed by type checkers. https://www.python.org/dev/peps/pep-0561/#packaging-type-information. See https://github.com/googleapis/python-secret-manager/issues/79 Source-Author: Bu Sun Kim <8822365+busunkim96@users.noreply.github.com> Source-Date: Fri Feb 5 17:32:06 2021 -0700 Source-Repo: googleapis/synthtool Source-Sha: 33366574ffb9e11737b3547eb6f020ecae0536e8 Source-Link: https://github.com/googleapis/synthtool/commit/33366574ffb9e11737b3547eb6f020ecae0536e8 --- MANIFEST.in | 4 ++-- synth.metadata | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index e9e29d12..e783f4c6 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -16,10 +16,10 @@ # Generated by synthtool. DO NOT EDIT! include README.rst LICENSE -recursive-include google *.json *.proto +recursive-include google *.json *.proto py.typed recursive-include tests * global-exclude *.py[co] global-exclude __pycache__ # Exclude scripts for samples readmegen -prune scripts/readme-gen \ No newline at end of file +prune scripts/readme-gen diff --git a/synth.metadata b/synth.metadata index 95a8d925..eb9a4988 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "d1bb9173100f62c0cfc8f3138b62241e7f47ca6a" + "sha": "33366574ffb9e11737b3547eb6f020ecae0536e8" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "d1bb9173100f62c0cfc8f3138b62241e7f47ca6a" + "sha": "33366574ffb9e11737b3547eb6f020ecae0536e8" } } ], From 9e3a0f1b4b5f1ed4d91eef28326fefbcd32d83f1 Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:23:12 -0700 Subject: [PATCH 38/47] docs: update python contributing guide Adds details about blacken, updates version for system tests, and shows how to pass through pytest arguments. Source-Author: Chris Cotter Source-Date: Mon Feb 8 17:13:36 2021 -0500 Source-Repo: googleapis/synthtool Source-Sha: 4679e7e415221f03ff2a71e3ffad75b9ec41d87e Source-Link: https://github.com/googleapis/synthtool/commit/4679e7e415221f03ff2a71e3ffad75b9ec41d87e --- CONTRIBUTING.rst | 22 ++++++++++++++++++---- synth.metadata | 4 ++-- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index ab65833f..dd8c5248 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -70,9 +70,14 @@ We use `nox `__ to instrument our tests. - To test your changes, run unit tests with ``nox``:: $ nox -s unit-2.7 - $ nox -s unit-3.7 + $ nox -s unit-3.8 $ ... +- Args to pytest can be passed through the nox command separated by a `--`. For + example, to run a single test:: + + $ nox -s unit-3.8 -- -k + .. note:: The unit tests and system tests are described in the @@ -93,8 +98,12 @@ On Debian/Ubuntu:: ************ Coding Style ************ +- We use the automatic code formatter ``black``. You can run it using + the nox session ``blacken``. This will eliminate many lint errors. Run via:: + + $ nox -s blacken -- PEP8 compliance, with exceptions defined in the linter configuration. +- PEP8 compliance is required, with exceptions defined in the linter configuration. If you have ``nox`` installed, you can test that you have not introduced any non-compliant code via:: @@ -133,13 +142,18 @@ Running System Tests - To run system tests, you can execute:: - $ nox -s system-3.7 + # Run all system tests + $ nox -s system-3.8 $ nox -s system-2.7 + # Run a single system test + $ nox -s system-3.8 -- -k + + .. note:: System tests are only configured to run under Python 2.7 and - Python 3.7. For expediency, we do not run them in older versions + Python 3.8. For expediency, we do not run them in older versions of Python 3. This alone will not run the tests. You'll need to change some local diff --git a/synth.metadata b/synth.metadata index eb9a4988..cb14da95 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "33366574ffb9e11737b3547eb6f020ecae0536e8" + "sha": "4679e7e415221f03ff2a71e3ffad75b9ec41d87e" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "33366574ffb9e11737b3547eb6f020ecae0536e8" + "sha": "4679e7e415221f03ff2a71e3ffad75b9ec41d87e" } } ], From 74d83541177c9d284781735f376b9754691bd691 Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:23:57 -0700 Subject: [PATCH 39/47] build(python): enable flakybot on library unit and system tests Source-Author: Bu Sun Kim <8822365+busunkim96@users.noreply.github.com> Source-Date: Wed Feb 17 14:10:46 2021 -0700 Source-Repo: googleapis/synthtool Source-Sha: d17674372e27fb8f23013935e794aa37502071aa Source-Link: https://github.com/googleapis/synthtool/commit/d17674372e27fb8f23013935e794aa37502071aa --- .gitignore | 4 +++- .kokoro/build.sh | 10 ++++++++++ noxfile.py | 17 +++++++++++++++-- synth.metadata | 4 ++-- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index b9daa52f..b4243ced 100644 --- a/.gitignore +++ b/.gitignore @@ -50,8 +50,10 @@ docs.metadata # Virtual environment env/ + +# Test logs coverage.xml -sponge_log.xml +*sponge_log.xml # System test environment variables. system_tests/local_test_setup diff --git a/.kokoro/build.sh b/.kokoro/build.sh index 84709de2..f4484f2a 100755 --- a/.kokoro/build.sh +++ b/.kokoro/build.sh @@ -40,6 +40,16 @@ python3 -m pip uninstall --yes --quiet nox-automation python3 -m pip install --upgrade --quiet nox python3 -m nox --version +# If this is a continuous build, send the test log to the FlakyBot. +# See https://github.com/googleapis/repo-automation-bots/tree/master/packages/flakybot. +if [[ $KOKORO_BUILD_ARTIFACTS_SUBDIR = *"continuous"* ]]; then + cleanup() { + chmod +x $KOKORO_GFILE_DIR/linux_amd64/flakybot + $KOKORO_GFILE_DIR/linux_amd64/flakybot + } + trap cleanup EXIT HUP +fi + # If NOX_SESSION is set, it only runs the specified session, # otherwise run all the sessions. if [[ -n "${NOX_SESSION:-}" ]]; then diff --git a/noxfile.py b/noxfile.py index 9e90799f..87c33ae0 100644 --- a/noxfile.py +++ b/noxfile.py @@ -93,6 +93,7 @@ def default(session): session.run( "py.test", "--quiet", + f"--junitxml=unit_{session.python}_sponge_log.xml", "--cov=google/cloud", "--cov=tests/unit", "--cov-append", @@ -141,9 +142,21 @@ def system(session): # Run py.test against the system tests. if system_test_exists: - session.run("py.test", "--quiet", system_test_path, *session.posargs) + session.run( + "py.test", + "--quiet", + f"--junitxml=system_{session.python}_sponge_log.xml", + system_test_path, + *session.posargs, + ) if system_test_folder_exists: - session.run("py.test", "--quiet", system_test_folder_path, *session.posargs) + session.run( + "py.test", + "--quiet", + f"--junitxml=system_{session.python}_sponge_log.xml", + system_test_folder_path, + *session.posargs, + ) @nox.session(python=DEFAULT_PYTHON_VERSION) diff --git a/synth.metadata b/synth.metadata index cb14da95..25e8c54e 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "4679e7e415221f03ff2a71e3ffad75b9ec41d87e" + "sha": "d17674372e27fb8f23013935e794aa37502071aa" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "4679e7e415221f03ff2a71e3ffad75b9ec41d87e" + "sha": "d17674372e27fb8f23013935e794aa37502071aa" } } ], From e6aa96e371368b3d7e8ab18f54b20539162cb578 Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:24:31 -0700 Subject: [PATCH 40/47] test: install pyopenssl for mtls testing Source-Author: arithmetic1728 <58957152+arithmetic1728@users.noreply.github.com> Source-Date: Tue Mar 2 12:27:56 2021 -0800 Source-Repo: googleapis/synthtool Source-Sha: 0780323da96d5a53925fe0547757181fe76e8f1e Source-Link: https://github.com/googleapis/synthtool/commit/0780323da96d5a53925fe0547757181fe76e8f1e --- noxfile.py | 3 +++ synth.metadata | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/noxfile.py b/noxfile.py index 87c33ae0..8d9724d0 100644 --- a/noxfile.py +++ b/noxfile.py @@ -123,6 +123,9 @@ def system(session): # Sanity check: Only run tests if the environment variable is set. if not os.environ.get("GOOGLE_APPLICATION_CREDENTIALS", ""): session.skip("Credentials must be set via environment variable") + # Install pyopenssl for mTLS testing. + if os.environ.get("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false") == "true": + session.install("pyopenssl") system_test_exists = os.path.exists(system_test_path) system_test_folder_exists = os.path.exists(system_test_folder_path) diff --git a/synth.metadata b/synth.metadata index 25e8c54e..704d1aa2 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "d17674372e27fb8f23013935e794aa37502071aa" + "sha": "0780323da96d5a53925fe0547757181fe76e8f1e" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "d17674372e27fb8f23013935e794aa37502071aa" + "sha": "0780323da96d5a53925fe0547757181fe76e8f1e" } } ], From baeabe6d6ea53f032525c87b0c542754d66798dd Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:25:29 -0700 Subject: [PATCH 41/47] chore: add pre-commit-config to renovate ignore paths Disable renovate PRs on the .pre-commit-config.yaml which is templated from synthtool. https://docs.renovatebot.com/configuration-options/#ignorepaths Source-Author: Bu Sun Kim <8822365+busunkim96@users.noreply.github.com> Source-Date: Mon Mar 15 09:05:39 2021 -0600 Source-Repo: googleapis/synthtool Source-Sha: 2c54c473779ea731128cea61a3a6c975a08a5378 Source-Link: https://github.com/googleapis/synthtool/commit/2c54c473779ea731128cea61a3a6c975a08a5378 --- renovate.json | 3 ++- synth.metadata | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/renovate.json b/renovate.json index 4fa94931..f08bc22c 100644 --- a/renovate.json +++ b/renovate.json @@ -1,5 +1,6 @@ { "extends": [ "config:base", ":preserveSemverRanges" - ] + ], + "ignorePaths": [".pre-commit-config.yaml"] } diff --git a/synth.metadata b/synth.metadata index 704d1aa2..0f374e1c 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "0780323da96d5a53925fe0547757181fe76e8f1e" + "sha": "2c54c473779ea731128cea61a3a6c975a08a5378" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "0780323da96d5a53925fe0547757181fe76e8f1e" + "sha": "2c54c473779ea731128cea61a3a6c975a08a5378" } } ], From 3bc4b8861617152cb7f33a6634023498baaf9cfd Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:25:41 -0700 Subject: [PATCH 42/47] build(python): fail nox sessions if a python version is missing Nox's default behavior is to quietly skip if a python interpreter is missing. https://nox.thea.codes/en/stable/usage.html#failing-sessions-when-the-interpreter-is-missing Source-Author: Bu Sun Kim <8822365+busunkim96@users.noreply.github.com> Source-Date: Tue Mar 16 13:38:02 2021 -0600 Source-Repo: googleapis/synthtool Source-Sha: eda422b90c3dde4a872a13e6b78a8f802c40d0db Source-Link: https://github.com/googleapis/synthtool/commit/eda422b90c3dde4a872a13e6b78a8f802c40d0db --- noxfile.py | 3 +++ synth.metadata | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/noxfile.py b/noxfile.py index 8d9724d0..9249c5b9 100644 --- a/noxfile.py +++ b/noxfile.py @@ -41,6 +41,9 @@ "docs", ] +# Error if a python version is missing +nox.options.error_on_missing_interpreters = True + @nox.session(python=DEFAULT_PYTHON_VERSION) def lint(session): diff --git a/synth.metadata b/synth.metadata index 0f374e1c..d0326e9b 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "2c54c473779ea731128cea61a3a6c975a08a5378" + "sha": "eda422b90c3dde4a872a13e6b78a8f802c40d0db" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "2c54c473779ea731128cea61a3a6c975a08a5378" + "sha": "eda422b90c3dde4a872a13e6b78a8f802c40d0db" } } ], From 34f5360d6d9170e550a9c2e85251a361d252938f Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:26:16 -0700 Subject: [PATCH 43/47] chore(python): add kokoro configs for periodic builds against head This change should be non-destructive. Note for library repo maintainers: After applying this change, you can easily add (or change) periodic builds against head by adding config files in google3. See python-pubsub repo for example. Source-Author: Takashi Matsuo Source-Date: Fri Mar 19 11:17:59 2021 -0700 Source-Repo: googleapis/synthtool Source-Sha: 79c8dd7ee768292f933012d3a69a5b4676404cda Source-Link: https://github.com/googleapis/synthtool/commit/79c8dd7ee768292f933012d3a69a5b4676404cda --- .kokoro/samples/python3.6/periodic-head.cfg | 11 +++ .kokoro/samples/python3.7/periodic-head.cfg | 11 +++ .kokoro/samples/python3.8/periodic-head.cfg | 11 +++ .kokoro/test-samples-against-head.sh | 28 ++++++ .kokoro/test-samples-impl.sh | 102 ++++++++++++++++++++ .kokoro/test-samples.sh | 96 +++--------------- synth.metadata | 9 +- 7 files changed, 186 insertions(+), 82 deletions(-) create mode 100644 .kokoro/samples/python3.6/periodic-head.cfg create mode 100644 .kokoro/samples/python3.7/periodic-head.cfg create mode 100644 .kokoro/samples/python3.8/periodic-head.cfg create mode 100755 .kokoro/test-samples-against-head.sh create mode 100755 .kokoro/test-samples-impl.sh diff --git a/.kokoro/samples/python3.6/periodic-head.cfg b/.kokoro/samples/python3.6/periodic-head.cfg new file mode 100644 index 00000000..f9cfcd33 --- /dev/null +++ b/.kokoro/samples/python3.6/periodic-head.cfg @@ -0,0 +1,11 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/python-pubsub/.kokoro/test-samples-against-head.sh" +} diff --git a/.kokoro/samples/python3.7/periodic-head.cfg b/.kokoro/samples/python3.7/periodic-head.cfg new file mode 100644 index 00000000..f9cfcd33 --- /dev/null +++ b/.kokoro/samples/python3.7/periodic-head.cfg @@ -0,0 +1,11 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/python-pubsub/.kokoro/test-samples-against-head.sh" +} diff --git a/.kokoro/samples/python3.8/periodic-head.cfg b/.kokoro/samples/python3.8/periodic-head.cfg new file mode 100644 index 00000000..f9cfcd33 --- /dev/null +++ b/.kokoro/samples/python3.8/periodic-head.cfg @@ -0,0 +1,11 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/python-pubsub/.kokoro/test-samples-against-head.sh" +} diff --git a/.kokoro/test-samples-against-head.sh b/.kokoro/test-samples-against-head.sh new file mode 100755 index 00000000..821d7944 --- /dev/null +++ b/.kokoro/test-samples-against-head.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# Copyright 2020 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 +# +# https://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 customized test runner for samples. +# +# For periodic builds, you can specify this file for testing against head. + +# `-e` enables the script to automatically fail when a command fails +# `-o pipefail` sets the exit code to the rightmost comment to exit with a non-zero +set -eo pipefail +# Enables `**` to include files nested inside sub-folders +shopt -s globstar + +cd github/python-trace + +exec .kokoro/test-samples-impl.sh diff --git a/.kokoro/test-samples-impl.sh b/.kokoro/test-samples-impl.sh new file mode 100755 index 00000000..cf5de74c --- /dev/null +++ b/.kokoro/test-samples-impl.sh @@ -0,0 +1,102 @@ +#!/bin/bash +# Copyright 2021 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 +# +# https://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. + + +# `-e` enables the script to automatically fail when a command fails +# `-o pipefail` sets the exit code to the rightmost comment to exit with a non-zero +set -eo pipefail +# Enables `**` to include files nested inside sub-folders +shopt -s globstar + +# Exit early if samples directory doesn't exist +if [ ! -d "./samples" ]; then + echo "No tests run. `./samples` not found" + exit 0 +fi + +# Disable buffering, so that the logs stream through. +export PYTHONUNBUFFERED=1 + +# Debug: show build environment +env | grep KOKORO + +# Install nox +python3.6 -m pip install --upgrade --quiet nox + +# Use secrets acessor service account to get secrets +if [[ -f "${KOKORO_GFILE_DIR}/secrets_viewer_service_account.json" ]]; then + gcloud auth activate-service-account \ + --key-file="${KOKORO_GFILE_DIR}/secrets_viewer_service_account.json" \ + --project="cloud-devrel-kokoro-resources" +fi + +# This script will create 3 files: +# - testing/test-env.sh +# - testing/service-account.json +# - testing/client-secrets.json +./scripts/decrypt-secrets.sh + +source ./testing/test-env.sh +export GOOGLE_APPLICATION_CREDENTIALS=$(pwd)/testing/service-account.json + +# For cloud-run session, we activate the service account for gcloud sdk. +gcloud auth activate-service-account \ + --key-file "${GOOGLE_APPLICATION_CREDENTIALS}" + +export GOOGLE_CLIENT_SECRETS=$(pwd)/testing/client-secrets.json + +echo -e "\n******************** TESTING PROJECTS ********************" + +# Switch to 'fail at end' to allow all tests to complete before exiting. +set +e +# Use RTN to return a non-zero value if the test fails. +RTN=0 +ROOT=$(pwd) +# Find all requirements.txt in the samples directory (may break on whitespace). +for file in samples/**/requirements.txt; do + cd "$ROOT" + # Navigate to the project folder. + file=$(dirname "$file") + cd "$file" + + echo "------------------------------------------------------------" + echo "- testing $file" + echo "------------------------------------------------------------" + + # Use nox to execute the tests for the project. + python3.6 -m nox -s "$RUN_TESTS_SESSION" + EXIT=$? + + # If this is a periodic build, send the test log to the FlakyBot. + # See https://github.com/googleapis/repo-automation-bots/tree/master/packages/flakybot. + if [[ $KOKORO_BUILD_ARTIFACTS_SUBDIR = *"periodic"* ]]; then + chmod +x $KOKORO_GFILE_DIR/linux_amd64/flakybot + $KOKORO_GFILE_DIR/linux_amd64/flakybot + fi + + if [[ $EXIT -ne 0 ]]; then + RTN=1 + echo -e "\n Testing failed: Nox returned a non-zero exit code. \n" + else + echo -e "\n Testing completed.\n" + fi + +done +cd "$ROOT" + +# Workaround for Kokoro permissions issue: delete secrets +rm testing/{test-env.sh,client-secrets.json,service-account.json} + +exit "$RTN" diff --git a/.kokoro/test-samples.sh b/.kokoro/test-samples.sh index 2e42e1dd..4fb9bf1f 100755 --- a/.kokoro/test-samples.sh +++ b/.kokoro/test-samples.sh @@ -13,6 +13,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +# The default test runner for samples. +# +# For periodic builds, we rewinds the repo to the latest release, and +# run test-samples-impl.sh. # `-e` enables the script to automatically fail when a command fails # `-o pipefail` sets the exit code to the rightmost comment to exit with a non-zero @@ -24,87 +28,19 @@ cd github/python-trace # Run periodic samples tests at latest release if [[ $KOKORO_BUILD_ARTIFACTS_SUBDIR = *"periodic"* ]]; then + # preserving the test runner implementation. + cp .kokoro/test-samples-impl.sh "${TMPDIR}/test-samples-impl.sh" + echo "--- IMPORTANT IMPORTANT IMPORTANT ---" + echo "Now we rewind the repo back to the latest release..." LATEST_RELEASE=$(git describe --abbrev=0 --tags) git checkout $LATEST_RELEASE -fi - -# Exit early if samples directory doesn't exist -if [ ! -d "./samples" ]; then - echo "No tests run. `./samples` not found" - exit 0 -fi - -# Disable buffering, so that the logs stream through. -export PYTHONUNBUFFERED=1 - -# Debug: show build environment -env | grep KOKORO - -# Install nox -python3.6 -m pip install --upgrade --quiet nox - -# Use secrets acessor service account to get secrets -if [[ -f "${KOKORO_GFILE_DIR}/secrets_viewer_service_account.json" ]]; then - gcloud auth activate-service-account \ - --key-file="${KOKORO_GFILE_DIR}/secrets_viewer_service_account.json" \ - --project="cloud-devrel-kokoro-resources" -fi - -# This script will create 3 files: -# - testing/test-env.sh -# - testing/service-account.json -# - testing/client-secrets.json -./scripts/decrypt-secrets.sh - -source ./testing/test-env.sh -export GOOGLE_APPLICATION_CREDENTIALS=$(pwd)/testing/service-account.json - -# For cloud-run session, we activate the service account for gcloud sdk. -gcloud auth activate-service-account \ - --key-file "${GOOGLE_APPLICATION_CREDENTIALS}" - -export GOOGLE_CLIENT_SECRETS=$(pwd)/testing/client-secrets.json - -echo -e "\n******************** TESTING PROJECTS ********************" - -# Switch to 'fail at end' to allow all tests to complete before exiting. -set +e -# Use RTN to return a non-zero value if the test fails. -RTN=0 -ROOT=$(pwd) -# Find all requirements.txt in the samples directory (may break on whitespace). -for file in samples/**/requirements.txt; do - cd "$ROOT" - # Navigate to the project folder. - file=$(dirname "$file") - cd "$file" - - echo "------------------------------------------------------------" - echo "- testing $file" - echo "------------------------------------------------------------" - - # Use nox to execute the tests for the project. - python3.6 -m nox -s "$RUN_TESTS_SESSION" - EXIT=$? - - # If this is a periodic build, send the test log to the FlakyBot. - # See https://github.com/googleapis/repo-automation-bots/tree/master/packages/flakybot. - if [[ $KOKORO_BUILD_ARTIFACTS_SUBDIR = *"periodic"* ]]; then - chmod +x $KOKORO_GFILE_DIR/linux_amd64/flakybot - $KOKORO_GFILE_DIR/linux_amd64/flakybot + echo "The current head is: " + echo $(git rev-parse --verify HEAD) + echo "--- IMPORTANT IMPORTANT IMPORTANT ---" + # move back the test runner implementation if there's no file. + if [ ! -f .kokoro/test-samples-impl.sh ]; then + cp "${TMPDIR}/test-samples-impl.sh" .kokoro/test-samples-impl.sh fi +fi - if [[ $EXIT -ne 0 ]]; then - RTN=1 - echo -e "\n Testing failed: Nox returned a non-zero exit code. \n" - else - echo -e "\n Testing completed.\n" - fi - -done -cd "$ROOT" - -# Workaround for Kokoro permissions issue: delete secrets -rm testing/{test-env.sh,client-secrets.json,service-account.json} - -exit "$RTN" +exec .kokoro/test-samples-impl.sh diff --git a/synth.metadata b/synth.metadata index d0326e9b..f704e273 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "eda422b90c3dde4a872a13e6b78a8f802c40d0db" + "sha": "79c8dd7ee768292f933012d3a69a5b4676404cda" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "eda422b90c3dde4a872a13e6b78a8f802c40d0db" + "sha": "79c8dd7ee768292f933012d3a69a5b4676404cda" } } ], @@ -83,16 +83,21 @@ ".kokoro/samples/lint/presubmit.cfg", ".kokoro/samples/python3.6/common.cfg", ".kokoro/samples/python3.6/continuous.cfg", + ".kokoro/samples/python3.6/periodic-head.cfg", ".kokoro/samples/python3.6/periodic.cfg", ".kokoro/samples/python3.6/presubmit.cfg", ".kokoro/samples/python3.7/common.cfg", ".kokoro/samples/python3.7/continuous.cfg", + ".kokoro/samples/python3.7/periodic-head.cfg", ".kokoro/samples/python3.7/periodic.cfg", ".kokoro/samples/python3.7/presubmit.cfg", ".kokoro/samples/python3.8/common.cfg", ".kokoro/samples/python3.8/continuous.cfg", + ".kokoro/samples/python3.8/periodic-head.cfg", ".kokoro/samples/python3.8/periodic.cfg", ".kokoro/samples/python3.8/presubmit.cfg", + ".kokoro/test-samples-against-head.sh", + ".kokoro/test-samples-impl.sh", ".kokoro/test-samples.sh", ".kokoro/trampoline.sh", ".kokoro/trampoline_v2.sh", From 6e3d5ae71acaeaacb98e58320d6597db66a6e583 Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:26:40 -0700 Subject: [PATCH 44/47] chore(deps): update precommit hook pycqa/flake8 to v3.9.0 [![WhiteSource Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [pycqa/flake8](https://gitlab.com/pycqa/flake8) | repository | minor | `3.8.4` -> `3.9.0` | --- ### Release Notes
pycqa/flake8 ### [`v3.9.0`](https://gitlab.com/pycqa/flake8/compare/3.8.4...3.9.0) [Compare Source](https://gitlab.com/pycqa/flake8/compare/3.8.4...3.9.0)
--- ### Renovate configuration :date: **Schedule**: At any time (no schedule defined). :vertical_traffic_light: **Automerge**: Disabled by config. Please merge this manually once you are satisfied. :recycle: **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. :no_bell: **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [WhiteSource Renovate](https://renovate.whitesourcesoftware.com). View repository job log [here](https://app.renovatebot.com/dashboard#github/googleapis/synthtool). Source-Author: WhiteSource Renovate Source-Date: Tue Mar 23 17:38:03 2021 +0100 Source-Repo: googleapis/synthtool Source-Sha: f5c5904fb0c6aa3b3730eadf4e5a4485afc65726 Source-Link: https://github.com/googleapis/synthtool/commit/f5c5904fb0c6aa3b3730eadf4e5a4485afc65726 --- .pre-commit-config.yaml | 2 +- synth.metadata | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a9024b15..32302e48 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -12,6 +12,6 @@ repos: hooks: - id: black - repo: https://gitlab.com/pycqa/flake8 - rev: 3.8.4 + rev: 3.9.0 hooks: - id: flake8 diff --git a/synth.metadata b/synth.metadata index f704e273..9623d840 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "79c8dd7ee768292f933012d3a69a5b4676404cda" + "sha": "f5c5904fb0c6aa3b3730eadf4e5a4485afc65726" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "79c8dd7ee768292f933012d3a69a5b4676404cda" + "sha": "f5c5904fb0c6aa3b3730eadf4e5a4485afc65726" } } ], From 8685deeca16bd59a8f3baadd8bbc964ddb0248c8 Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Tue, 23 Mar 2021 15:26:40 -0700 Subject: [PATCH 45/47] test(python): use constraints files to check dependency lower bounds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use a constraints file when installing dependencies for system and unit tests nox sessions. https://pip.pypa.io/en/stable/user_guide/#constraints-files > Constraints files are requirements files that **only control which version of a requirement is installed, not whether it is installed or not**. Their syntax and contents is nearly identical to Requirements Files. There is one key difference: Including a package in a constraints file does not trigger installation of the package. ``` testing ├── constraints-3.10.txt ├── constraints-3.11.txt ├── constraints-3.6.txt ├── constraints-3.7.txt ├── constraints-3.8.txt └── constraints-3.9.txt ``` Going forward, one constraints file (currently 3.6) will be populated with every library requirement and extra listed in the `setup.py`. The constraints file will pin each requirement to the lower bound. This ensures that library maintainers will see test failures if they forget to update a lower bound on a dependency. See https://github.com/googleapis/python-bigquery/pull/263 for an example Source-Author: Bu Sun Kim <8822365+busunkim96@users.noreply.github.com> Source-Date: Tue Mar 23 10:52:02 2021 -0600 Source-Repo: googleapis/synthtool Source-Sha: 86ed43d4f56e6404d068e62e497029018879c771 Source-Link: https://github.com/googleapis/synthtool/commit/86ed43d4f56e6404d068e62e497029018879c771 --- noxfile.py | 22 ++++++++++++++-------- synth.metadata | 4 ++-- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/noxfile.py b/noxfile.py index 9249c5b9..8e9b9413 100644 --- a/noxfile.py +++ b/noxfile.py @@ -18,6 +18,7 @@ from __future__ import absolute_import import os +import pathlib import shutil import nox @@ -30,6 +31,8 @@ SYSTEM_TEST_PYTHON_VERSIONS = ["3.8"] UNIT_TEST_PYTHON_VERSIONS = ["3.6", "3.7", "3.8", "3.9"] +CURRENT_DIRECTORY = pathlib.Path(__file__).parent.absolute() + # 'docfx' is excluded since it only needs to run in 'docs-presubmit' nox.options.sessions = [ "unit", @@ -84,13 +87,15 @@ def lint_setup_py(session): def default(session): # Install all test dependencies, then install this package in-place. - session.install("asyncmock", "pytest-asyncio") - session.install( - "mock", "pytest", "pytest-cov", + constraints_path = str( + CURRENT_DIRECTORY / "testing" / f"constraints-{session.python}.txt" ) + session.install("asyncmock", "pytest-asyncio", "-c", constraints_path) - session.install("-e", ".") + session.install("mock", "pytest", "pytest-cov", "-c", constraints_path) + + session.install("-e", ".", "-c", constraints_path) # Run py.test against the unit tests. session.run( @@ -117,6 +122,9 @@ def unit(session): @nox.session(python=SYSTEM_TEST_PYTHON_VERSIONS) def system(session): """Run the system test suite.""" + constraints_path = str( + CURRENT_DIRECTORY / "testing" / f"constraints-{session.python}.txt" + ) system_test_path = os.path.join("tests", "system.py") system_test_folder_path = os.path.join("tests", "system") @@ -141,10 +149,8 @@ def system(session): # Install all test dependencies, then install this package into the # virtualenv's dist-packages. - session.install( - "mock", "pytest", "google-cloud-testutils", - ) - session.install("-e", ".") + session.install("mock", "pytest", "google-cloud-testutils", "-c", constraints_path) + session.install("-e", ".", "-c", constraints_path) # Run py.test against the system tests. if system_test_exists: diff --git a/synth.metadata b/synth.metadata index 9623d840..e05c458a 100644 --- a/synth.metadata +++ b/synth.metadata @@ -19,14 +19,14 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "f5c5904fb0c6aa3b3730eadf4e5a4485afc65726" + "sha": "86ed43d4f56e6404d068e62e497029018879c771" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "f5c5904fb0c6aa3b3730eadf4e5a4485afc65726" + "sha": "86ed43d4f56e6404d068e62e497029018879c771" } } ], From 1806034ea0beded6473de7d0938b92243d653c4f Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Fri, 2 Apr 2021 22:52:52 +0000 Subject: [PATCH 46/47] fix: fix namespacing --- .coveragerc | 2 +- README.rst | 4 +- docs/cloudtrace_v1/services.rst | 6 - docs/cloudtrace_v1/trace_service.rst | 11 - docs/cloudtrace_v1/types.rst | 7 - docs/cloudtrace_v2/services.rst | 6 - docs/cloudtrace_v2/types.rst | 7 - docs/trace_v1/services.rst | 6 +- docs/trace_v1/trace_service.rst | 11 + docs/trace_v1/types.rst | 2 + docs/trace_v2/services.rst | 6 +- .../trace_service.rst | 2 +- docs/trace_v2/types.rst | 2 + .../trace_v1}/__init__.py | 0 .../services/trace_service/async_client.py | 113 +- .../trace_v1/services/trace_service/client.py | 205 ++- .../trace_v1/services/trace_service/pagers.py | 27 +- .../trace_service/transports/__init__.py | 1 - .../services/trace_service/transports/base.py | 23 +- .../services/trace_service/transports/grpc.py | 130 +- .../trace_service/transports/grpc_asyncio.py | 117 +- google/cloud/trace_v1/types/__init__.py | 17 +- google/cloud/trace_v1/types/trace.py | 28 +- .../trace_v2}/__init__.py | 0 .../services/trace_service/async_client.py | 104 +- .../trace_v2/services/trace_service/client.py | 192 ++- .../trace_service/transports/__init__.py | 1 - .../services/trace_service/transports/base.py | 21 +- .../services/trace_service/transports/grpc.py | 130 +- .../trace_service/transports/grpc_asyncio.py | 117 +- google/cloud/trace_v2/types/__init__.py | 9 +- google/cloud/trace_v2/types/trace.py | 68 +- google/cloud/trace_v2/types/tracing.py | 2 +- google/devtools/cloudtrace/__init__.py | 40 - google/devtools/cloudtrace/py.typed | 2 - google/devtools/cloudtrace_v1/py.typed | 2 - .../cloudtrace_v1/services/__init__.py | 16 - .../services/trace_service/__init__.py | 24 - .../services/trace_service/async_client.py | 438 ----- .../services/trace_service/client.py | 577 ------- .../services/trace_service/pagers.py | 157 -- .../trace_service/transports/__init__.py | 35 - .../services/trace_service/transports/base.py | 188 -- .../services/trace_service/transports/grpc.py | 312 ---- .../trace_service/transports/grpc_asyncio.py | 318 ---- .../devtools/cloudtrace_v1/types/__init__.py | 36 - google/devtools/cloudtrace_v1/types/trace.py | 329 ---- google/devtools/cloudtrace_v2/py.typed | 2 - .../cloudtrace_v2/services/__init__.py | 16 - .../services/trace_service/__init__.py | 24 - .../services/trace_service/async_client.py | 341 ---- .../services/trace_service/client.py | 509 ------ .../trace_service/transports/__init__.py | 35 - .../services/trace_service/transports/base.py | 151 -- .../services/trace_service/transports/grpc.py | 283 ---- .../trace_service/transports/grpc_asyncio.py | 287 ---- .../devtools/cloudtrace_v2/types/__init__.py | 34 - google/devtools/cloudtrace_v2/types/trace.py | 513 ------ .../devtools/cloudtrace_v2/types/tracing.py | 47 - noxfile.py | 6 +- scripts/fixup_cloudtrace_v1_keywords.py | 181 -- scripts/fixup_cloudtrace_v2_keywords.py | 180 -- scripts/fixup_trace_v1_keywords.py | 1 + scripts/fixup_trace_v2_keywords.py | 1 + setup.py | 2 +- synth.metadata | 137 +- synth.py | 1 + testing/constraints-3.6.txt | 4 +- .../gapic/v1/test_system_trace_service_v1.py | 27 - .../v1/test_system_trace_service_v1_vpcsc.py | 76 - .../gapic/v2/test_system_trace_service_v2.py | 28 - .../v2/test_system_trace_service_v2_vpcsc.py | 47 - tests/unit/gapic/cloudtrace_v1/__init__.py | 16 - .../gapic/cloudtrace_v1/test_trace_service.py | 1507 ----------------- tests/unit/gapic/cloudtrace_v2/__init__.py | 16 - .../gapic/cloudtrace_v2/test_trace_service.py | 1316 -------------- tests/unit/gapic/trace_v1/__init__.py | 15 + .../unit/gapic/trace_v1/test_trace_service.py | 784 ++++++--- tests/unit/gapic/trace_v2/__init__.py | 15 + .../unit/gapic/trace_v2/test_trace_service.py | 736 +++++--- 80 files changed, 1922 insertions(+), 9267 deletions(-) delete mode 100644 docs/cloudtrace_v1/services.rst delete mode 100644 docs/cloudtrace_v1/trace_service.rst delete mode 100644 docs/cloudtrace_v1/types.rst delete mode 100644 docs/cloudtrace_v2/services.rst delete mode 100644 docs/cloudtrace_v2/types.rst create mode 100644 docs/trace_v1/trace_service.rst rename docs/{cloudtrace_v2 => trace_v2}/trace_service.rst (54%) rename google/{devtools/cloudtrace_v1 => cloud/trace_v1}/__init__.py (100%) rename google/{devtools/cloudtrace_v2 => cloud/trace_v2}/__init__.py (100%) delete mode 100644 google/devtools/cloudtrace/__init__.py delete mode 100644 google/devtools/cloudtrace/py.typed delete mode 100644 google/devtools/cloudtrace_v1/py.typed delete mode 100644 google/devtools/cloudtrace_v1/services/__init__.py delete mode 100644 google/devtools/cloudtrace_v1/services/trace_service/__init__.py delete mode 100644 google/devtools/cloudtrace_v1/services/trace_service/async_client.py delete mode 100644 google/devtools/cloudtrace_v1/services/trace_service/client.py delete mode 100644 google/devtools/cloudtrace_v1/services/trace_service/pagers.py delete mode 100644 google/devtools/cloudtrace_v1/services/trace_service/transports/__init__.py delete mode 100644 google/devtools/cloudtrace_v1/services/trace_service/transports/base.py delete mode 100644 google/devtools/cloudtrace_v1/services/trace_service/transports/grpc.py delete mode 100644 google/devtools/cloudtrace_v1/services/trace_service/transports/grpc_asyncio.py delete mode 100644 google/devtools/cloudtrace_v1/types/__init__.py delete mode 100644 google/devtools/cloudtrace_v1/types/trace.py delete mode 100644 google/devtools/cloudtrace_v2/py.typed delete mode 100644 google/devtools/cloudtrace_v2/services/__init__.py delete mode 100644 google/devtools/cloudtrace_v2/services/trace_service/__init__.py delete mode 100644 google/devtools/cloudtrace_v2/services/trace_service/async_client.py delete mode 100644 google/devtools/cloudtrace_v2/services/trace_service/client.py delete mode 100644 google/devtools/cloudtrace_v2/services/trace_service/transports/__init__.py delete mode 100644 google/devtools/cloudtrace_v2/services/trace_service/transports/base.py delete mode 100644 google/devtools/cloudtrace_v2/services/trace_service/transports/grpc.py delete mode 100644 google/devtools/cloudtrace_v2/services/trace_service/transports/grpc_asyncio.py delete mode 100644 google/devtools/cloudtrace_v2/types/__init__.py delete mode 100644 google/devtools/cloudtrace_v2/types/trace.py delete mode 100644 google/devtools/cloudtrace_v2/types/tracing.py delete mode 100644 scripts/fixup_cloudtrace_v1_keywords.py delete mode 100644 scripts/fixup_cloudtrace_v2_keywords.py delete mode 100644 tests/system/gapic/v1/test_system_trace_service_v1.py delete mode 100644 tests/system/gapic/v1/test_system_trace_service_v1_vpcsc.py delete mode 100644 tests/system/gapic/v2/test_system_trace_service_v2.py delete mode 100644 tests/system/gapic/v2/test_system_trace_service_v2_vpcsc.py delete mode 100644 tests/unit/gapic/cloudtrace_v1/__init__.py delete mode 100644 tests/unit/gapic/cloudtrace_v1/test_trace_service.py delete mode 100644 tests/unit/gapic/cloudtrace_v2/__init__.py delete mode 100644 tests/unit/gapic/cloudtrace_v2/test_trace_service.py diff --git a/.coveragerc b/.coveragerc index d348d12f..054c95ee 100644 --- a/.coveragerc +++ b/.coveragerc @@ -5,7 +5,7 @@ branch = True fail_under = 100 show_missing = True omit = - google/devtools/cloudtrace/__init__.py + google/cloud/trace/__init__.py exclude_lines = # Re-enable the standard pragma pragma: NO COVER diff --git a/README.rst b/README.rst index 29b312a3..eee79bd6 100644 --- a/README.rst +++ b/README.rst @@ -1,4 +1,4 @@ -Python Client for Google Devtools Cloudtrace API +Python Client for Google Cloud Trace API ================================================= Quick Start @@ -8,7 +8,7 @@ In order to use this library, you first need to go through the following steps: 1. `Select or create a Cloud Platform project.`_ 2. `Enable billing for your project.`_ -3. Enable the Google Devtools Cloudtrace API. +3. Enable the Google Cloud Trace API. 4. `Setup Authentication.`_ .. _Select or create a Cloud Platform project.: https://console.cloud.google.com/project diff --git a/docs/cloudtrace_v1/services.rst b/docs/cloudtrace_v1/services.rst deleted file mode 100644 index b143d452..00000000 --- a/docs/cloudtrace_v1/services.rst +++ /dev/null @@ -1,6 +0,0 @@ -Services for Google Devtools Cloudtrace v1 API -============================================== -.. toctree:: - :maxdepth: 2 - - trace_service diff --git a/docs/cloudtrace_v1/trace_service.rst b/docs/cloudtrace_v1/trace_service.rst deleted file mode 100644 index 522d3f8c..00000000 --- a/docs/cloudtrace_v1/trace_service.rst +++ /dev/null @@ -1,11 +0,0 @@ -TraceService ------------------------------- - -.. automodule:: google.devtools.cloudtrace_v1.services.trace_service - :members: - :inherited-members: - - -.. automodule:: google.devtools.cloudtrace_v1.services.trace_service.pagers - :members: - :inherited-members: diff --git a/docs/cloudtrace_v1/types.rst b/docs/cloudtrace_v1/types.rst deleted file mode 100644 index 1ece94cd..00000000 --- a/docs/cloudtrace_v1/types.rst +++ /dev/null @@ -1,7 +0,0 @@ -Types for Google Devtools Cloudtrace v1 API -=========================================== - -.. automodule:: google.devtools.cloudtrace_v1.types - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/cloudtrace_v2/services.rst b/docs/cloudtrace_v2/services.rst deleted file mode 100644 index 9233e023..00000000 --- a/docs/cloudtrace_v2/services.rst +++ /dev/null @@ -1,6 +0,0 @@ -Services for Google Devtools Cloudtrace v2 API -============================================== -.. toctree:: - :maxdepth: 2 - - trace_service diff --git a/docs/cloudtrace_v2/types.rst b/docs/cloudtrace_v2/types.rst deleted file mode 100644 index afdd0fe9..00000000 --- a/docs/cloudtrace_v2/types.rst +++ /dev/null @@ -1,7 +0,0 @@ -Types for Google Devtools Cloudtrace v2 API -=========================================== - -.. automodule:: google.devtools.cloudtrace_v2.types - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/trace_v1/services.rst b/docs/trace_v1/services.rst index e900d74b..ed26586e 100644 --- a/docs/trace_v1/services.rst +++ b/docs/trace_v1/services.rst @@ -1,6 +1,6 @@ Services for Google Cloud Trace v1 API ====================================== +.. toctree:: + :maxdepth: 2 -.. automodule:: google.cloud.trace_v1.services.trace_service - :members: - :inherited-members: + trace_service diff --git a/docs/trace_v1/trace_service.rst b/docs/trace_v1/trace_service.rst new file mode 100644 index 00000000..14bcf291 --- /dev/null +++ b/docs/trace_v1/trace_service.rst @@ -0,0 +1,11 @@ +TraceService +------------------------------ + +.. automodule:: google.cloud.trace_v1.services.trace_service + :members: + :inherited-members: + + +.. automodule:: google.cloud.trace_v1.services.trace_service.pagers + :members: + :inherited-members: diff --git a/docs/trace_v1/types.rst b/docs/trace_v1/types.rst index 3055682c..761d1e1f 100644 --- a/docs/trace_v1/types.rst +++ b/docs/trace_v1/types.rst @@ -3,3 +3,5 @@ Types for Google Cloud Trace v1 API .. automodule:: google.cloud.trace_v1.types :members: + :undoc-members: + :show-inheritance: diff --git a/docs/trace_v2/services.rst b/docs/trace_v2/services.rst index ce3705dd..d3d580ef 100644 --- a/docs/trace_v2/services.rst +++ b/docs/trace_v2/services.rst @@ -1,6 +1,6 @@ Services for Google Cloud Trace v2 API ====================================== +.. toctree:: + :maxdepth: 2 -.. automodule:: google.cloud.trace_v2.services.trace_service - :members: - :inherited-members: + trace_service diff --git a/docs/cloudtrace_v2/trace_service.rst b/docs/trace_v2/trace_service.rst similarity index 54% rename from docs/cloudtrace_v2/trace_service.rst rename to docs/trace_v2/trace_service.rst index c8e912b4..3fb8cc16 100644 --- a/docs/cloudtrace_v2/trace_service.rst +++ b/docs/trace_v2/trace_service.rst @@ -1,6 +1,6 @@ TraceService ------------------------------ -.. automodule:: google.devtools.cloudtrace_v2.services.trace_service +.. automodule:: google.cloud.trace_v2.services.trace_service :members: :inherited-members: diff --git a/docs/trace_v2/types.rst b/docs/trace_v2/types.rst index b5e205ce..3bd88c61 100644 --- a/docs/trace_v2/types.rst +++ b/docs/trace_v2/types.rst @@ -3,3 +3,5 @@ Types for Google Cloud Trace v2 API .. automodule:: google.cloud.trace_v2.types :members: + :undoc-members: + :show-inheritance: diff --git a/google/devtools/cloudtrace_v1/__init__.py b/google/cloud/trace_v1/__init__.py similarity index 100% rename from google/devtools/cloudtrace_v1/__init__.py rename to google/cloud/trace_v1/__init__.py diff --git a/google/cloud/trace_v1/services/trace_service/async_client.py b/google/cloud/trace_v1/services/trace_service/async_client.py index fb9606b2..64869c98 100644 --- a/google/cloud/trace_v1/services/trace_service/async_client.py +++ b/google/cloud/trace_v1/services/trace_service/async_client.py @@ -50,9 +50,72 @@ class TraceServiceAsyncClient: DEFAULT_ENDPOINT = TraceServiceClient.DEFAULT_ENDPOINT DEFAULT_MTLS_ENDPOINT = TraceServiceClient.DEFAULT_MTLS_ENDPOINT - from_service_account_file = TraceServiceClient.from_service_account_file + common_billing_account_path = staticmethod( + TraceServiceClient.common_billing_account_path + ) + parse_common_billing_account_path = staticmethod( + TraceServiceClient.parse_common_billing_account_path + ) + + common_folder_path = staticmethod(TraceServiceClient.common_folder_path) + parse_common_folder_path = staticmethod(TraceServiceClient.parse_common_folder_path) + + common_organization_path = staticmethod(TraceServiceClient.common_organization_path) + parse_common_organization_path = staticmethod( + TraceServiceClient.parse_common_organization_path + ) + + common_project_path = staticmethod(TraceServiceClient.common_project_path) + parse_common_project_path = staticmethod( + TraceServiceClient.parse_common_project_path + ) + + common_location_path = staticmethod(TraceServiceClient.common_location_path) + parse_common_location_path = staticmethod( + TraceServiceClient.parse_common_location_path + ) + + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + TraceServiceAsyncClient: The constructed client. + """ + return TraceServiceClient.from_service_account_info.__func__(TraceServiceAsyncClient, info, *args, **kwargs) # type: ignore + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + TraceServiceAsyncClient: The constructed client. + """ + return TraceServiceClient.from_service_account_file.__func__(TraceServiceAsyncClient, filename, *args, **kwargs) # type: ignore + from_service_account_json = from_service_account_file + @property + def transport(self) -> TraceServiceTransport: + """Return the transport used by the client instance. + + Returns: + TraceServiceTransport: The transport used by the client instance. + """ + return self._client.transport + get_transport_class = functools.partial( type(TraceServiceClient).get_transport_class, type(TraceServiceClient) ) @@ -79,16 +142,19 @@ def __init__( client_options (ClientOptions): Custom options for the client. It won't take effect if a ``transport`` instance is provided. (1) The ``api_endpoint`` property can be used to override the - default endpoint provided by the client. GOOGLE_API_USE_MTLS + default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT environment variable can also be used to override the endpoint: "always" (always use the default mTLS endpoint), "never" (always - use the default regular endpoint, this is the default value for - the environment variable) and "auto" (auto switch to the default - mTLS endpoint if client SSL credentials is present). However, - the ``api_endpoint`` property takes precedence if provided. - (2) The ``client_cert_source`` property is used to provide client - SSL credentials for mutual TLS transport. If not provided, the - default SSL credentials will be used if present. + use the default regular endpoint) and "auto" (auto switch to the + default mTLS endpoint if client certificate is present, this is + the default value). However, the ``api_endpoint`` property takes + precedence if provided. + (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable + is "true", then the ``client_cert_source`` property can be used + to provide client certificate for mutual TLS transport. If + not provided, the default SSL client certificate will be used if + present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not + set, no client certificate will be used. Raises: google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport @@ -115,13 +181,14 @@ async def list_traces( filter conditions. Args: - request (:class:`~.trace.ListTracesRequest`): + request (:class:`google.cloud.trace_v1.types.ListTracesRequest`): The request object. The request message for the `ListTraces` method. All fields are required unless specified. project_id (:class:`str`): Required. ID of the Cloud project where the trace data is stored. + This corresponds to the ``project_id`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -133,8 +200,8 @@ async def list_traces( sent along with the request as metadata. Returns: - ~.pagers.ListTracesAsyncPager: - The response message for the ``ListTraces`` method. + google.cloud.trace_v1.services.trace_service.pagers.ListTracesAsyncPager: + The response message for the ListTraces method. Iterating over this object will yield results and resolve additional pages automatically. @@ -143,7 +210,8 @@ async def list_traces( # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - if request is not None and any([project_id]): + has_flattened_params = any([project_id]) + if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " "the individual field arguments should be set." @@ -168,6 +236,7 @@ async def list_traces( predicate=retries.if_exception_type( exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, ), + deadline=45.0, ), default_timeout=45.0, client_info=DEFAULT_CLIENT_INFO, @@ -198,12 +267,13 @@ async def get_trace( r"""Gets a single trace by its ID. Args: - request (:class:`~.trace.GetTraceRequest`): + request (:class:`google.cloud.trace_v1.types.GetTraceRequest`): The request object. The request message for the `GetTrace` method. project_id (:class:`str`): Required. ID of the Cloud project where the trace data is stored. + This corresponds to the ``project_id`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -220,7 +290,7 @@ async def get_trace( sent along with the request as metadata. Returns: - ~.trace.Trace: + google.cloud.trace_v1.types.Trace: A trace describes how long it takes for an application to perform an operation. It consists of a set of @@ -231,7 +301,8 @@ async def get_trace( # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - if request is not None and any([project_id, trace_id]): + has_flattened_params = any([project_id, trace_id]) + if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " "the individual field arguments should be set." @@ -258,6 +329,7 @@ async def get_trace( predicate=retries.if_exception_type( exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, ), + deadline=45.0, ), default_timeout=45.0, client_info=DEFAULT_CLIENT_INFO, @@ -288,16 +360,17 @@ async def patch_traces( a new trace is created. Args: - request (:class:`~.trace.PatchTracesRequest`): + request (:class:`google.cloud.trace_v1.types.PatchTracesRequest`): The request object. The request message for the `PatchTraces` method. project_id (:class:`str`): Required. ID of the Cloud project where the trace data is stored. + This corresponds to the ``project_id`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - traces (:class:`~.trace.Traces`): + traces (:class:`google.cloud.trace_v1.types.Traces`): Required. The body of the message. This corresponds to the ``traces`` field on the ``request`` instance; if ``request`` is provided, this @@ -312,7 +385,8 @@ async def patch_traces( # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - if request is not None and any([project_id, traces]): + has_flattened_params = any([project_id, traces]) + if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " "the individual field arguments should be set." @@ -339,6 +413,7 @@ async def patch_traces( predicate=retries.if_exception_type( exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, ), + deadline=45.0, ), default_timeout=45.0, client_info=DEFAULT_CLIENT_INFO, diff --git a/google/cloud/trace_v1/services/trace_service/client.py b/google/cloud/trace_v1/services/trace_service/client.py index 77e2877c..6e182a33 100644 --- a/google/cloud/trace_v1/services/trace_service/client.py +++ b/google/cloud/trace_v1/services/trace_service/client.py @@ -16,17 +16,19 @@ # from collections import OrderedDict +from distutils import util import os import re -from typing import Callable, Dict, Sequence, Tuple, Type, Union +from typing import Callable, Dict, Optional, Sequence, Tuple, Type, Union import pkg_resources -import google.api_core.client_options as ClientOptions # type: ignore +from google.api_core import client_options as client_options_lib # type: ignore from google.api_core import exceptions # type: ignore from google.api_core import gapic_v1 # type: ignore from google.api_core import retry as retries # type: ignore from google.auth import credentials # type: ignore from google.auth.transport import mtls # type: ignore +from google.auth.transport.grpc import SslCredentials # type: ignore from google.auth.exceptions import MutualTLSChannelError # type: ignore from google.oauth2 import service_account # type: ignore @@ -112,6 +114,22 @@ def _get_default_mtls_endpoint(api_endpoint): DEFAULT_ENDPOINT ) + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + TraceServiceClient: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_info(info) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + @classmethod def from_service_account_file(cls, filename: str, *args, **kwargs): """Creates an instance of this client using the provided credentials @@ -124,7 +142,7 @@ def from_service_account_file(cls, filename: str, *args, **kwargs): kwargs: Additional arguments to pass to the constructor. Returns: - {@api.name}: The constructed client. + TraceServiceClient: The constructed client. """ credentials = service_account.Credentials.from_service_account_file(filename) kwargs["credentials"] = credentials @@ -132,12 +150,80 @@ def from_service_account_file(cls, filename: str, *args, **kwargs): from_service_account_json = from_service_account_file + @property + def transport(self) -> TraceServiceTransport: + """Return the transport used by the client instance. + + Returns: + TraceServiceTransport: The transport used by the client instance. + """ + return self._transport + + @staticmethod + def common_billing_account_path(billing_account: str,) -> str: + """Return a fully-qualified billing_account string.""" + return "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + + @staticmethod + def parse_common_billing_account_path(path: str) -> Dict[str, str]: + """Parse a billing_account path into its component segments.""" + m = re.match(r"^billingAccounts/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_folder_path(folder: str,) -> str: + """Return a fully-qualified folder string.""" + return "folders/{folder}".format(folder=folder,) + + @staticmethod + def parse_common_folder_path(path: str) -> Dict[str, str]: + """Parse a folder path into its component segments.""" + m = re.match(r"^folders/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_organization_path(organization: str,) -> str: + """Return a fully-qualified organization string.""" + return "organizations/{organization}".format(organization=organization,) + + @staticmethod + def parse_common_organization_path(path: str) -> Dict[str, str]: + """Parse a organization path into its component segments.""" + m = re.match(r"^organizations/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_project_path(project: str,) -> str: + """Return a fully-qualified project string.""" + return "projects/{project}".format(project=project,) + + @staticmethod + def parse_common_project_path(path: str) -> Dict[str, str]: + """Parse a project path into its component segments.""" + m = re.match(r"^projects/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_location_path(project: str, location: str,) -> str: + """Return a fully-qualified location string.""" + return "projects/{project}/locations/{location}".format( + project=project, location=location, + ) + + @staticmethod + def parse_common_location_path(path: str) -> Dict[str, str]: + """Parse a location path into its component segments.""" + m = re.match(r"^projects/(?P.+?)/locations/(?P.+?)$", path) + return m.groupdict() if m else {} + def __init__( self, *, - credentials: credentials.Credentials = None, - transport: Union[str, TraceServiceTransport] = None, - client_options: ClientOptions = None, + credentials: Optional[credentials.Credentials] = None, + transport: Union[str, TraceServiceTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: """Instantiate the trace service client. @@ -148,26 +234,29 @@ def __init__( credentials identify the application to the service; if none are specified, the client will attempt to ascertain the credentials from the environment. - transport (Union[str, ~.TraceServiceTransport]): The + transport (Union[str, TraceServiceTransport]): The transport to use. If set to None, a transport is chosen automatically. - client_options (ClientOptions): Custom options for the client. It - won't take effect if a ``transport`` instance is provided. + client_options (google.api_core.client_options.ClientOptions): Custom options for the + client. It won't take effect if a ``transport`` instance is provided. (1) The ``api_endpoint`` property can be used to override the - default endpoint provided by the client. GOOGLE_API_USE_MTLS + default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT environment variable can also be used to override the endpoint: "always" (always use the default mTLS endpoint), "never" (always - use the default regular endpoint, this is the default value for - the environment variable) and "auto" (auto switch to the default - mTLS endpoint if client SSL credentials is present). However, - the ``api_endpoint`` property takes precedence if provided. - (2) The ``client_cert_source`` property is used to provide client - SSL credentials for mutual TLS transport. If not provided, the - default SSL credentials will be used if present. - client_info (google.api_core.gapic_v1.client_info.ClientInfo): - The client info used to send a user-agent string along with - API requests. If ``None``, then default info will be used. - Generally, you only need to set this if you're developing + use the default regular endpoint) and "auto" (auto switch to the + default mTLS endpoint if client certificate is present, this is + the default value). However, the ``api_endpoint`` property takes + precedence if provided. + (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable + is "true", then the ``client_cert_source`` property can be used + to provide client certificate for mutual TLS transport. If + not provided, the default SSL client certificate will be used if + present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not + set, no client certificate will be used. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing your own client library. Raises: @@ -175,29 +264,43 @@ def __init__( creation failed for any reason. """ if isinstance(client_options, dict): - client_options = ClientOptions.from_dict(client_options) + client_options = client_options_lib.from_dict(client_options) if client_options is None: - client_options = ClientOptions.ClientOptions() + client_options = client_options_lib.ClientOptions() + + # Create SSL credentials for mutual TLS if needed. + use_client_cert = bool( + util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) + ) + + client_cert_source_func = None + is_mtls = False + if use_client_cert: + if client_options.client_cert_source: + is_mtls = True + client_cert_source_func = client_options.client_cert_source + else: + is_mtls = mtls.has_default_client_cert_source() + client_cert_source_func = ( + mtls.default_client_cert_source() if is_mtls else None + ) - if client_options.api_endpoint is None: - use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS", "never") + # Figure out which api endpoint to use. + if client_options.api_endpoint is not None: + api_endpoint = client_options.api_endpoint + else: + use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto") if use_mtls_env == "never": - client_options.api_endpoint = self.DEFAULT_ENDPOINT + api_endpoint = self.DEFAULT_ENDPOINT elif use_mtls_env == "always": - client_options.api_endpoint = self.DEFAULT_MTLS_ENDPOINT + api_endpoint = self.DEFAULT_MTLS_ENDPOINT elif use_mtls_env == "auto": - has_client_cert_source = ( - client_options.client_cert_source is not None - or mtls.has_default_client_cert_source() - ) - client_options.api_endpoint = ( - self.DEFAULT_MTLS_ENDPOINT - if has_client_cert_source - else self.DEFAULT_ENDPOINT + api_endpoint = ( + self.DEFAULT_MTLS_ENDPOINT if is_mtls else self.DEFAULT_ENDPOINT ) else: raise MutualTLSChannelError( - "Unsupported GOOGLE_API_USE_MTLS value. Accepted values: never, auto, always" + "Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted values: never, auto, always" ) # Save or instantiate the transport. @@ -221,10 +324,9 @@ def __init__( self._transport = Transport( credentials=credentials, credentials_file=client_options.credentials_file, - host=client_options.api_endpoint, + host=api_endpoint, scopes=client_options.scopes, - api_mtls_endpoint=client_options.api_endpoint, - client_cert_source=client_options.client_cert_source, + client_cert_source_for_mtls=client_cert_source_func, quota_project_id=client_options.quota_project_id, client_info=client_info, ) @@ -242,13 +344,14 @@ def list_traces( filter conditions. Args: - request (:class:`~.trace.ListTracesRequest`): + request (google.cloud.trace_v1.types.ListTracesRequest): The request object. The request message for the `ListTraces` method. All fields are required unless specified. - project_id (:class:`str`): + project_id (str): Required. ID of the Cloud project where the trace data is stored. + This corresponds to the ``project_id`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -260,8 +363,8 @@ def list_traces( sent along with the request as metadata. Returns: - ~.pagers.ListTracesPager: - The response message for the ``ListTraces`` method. + google.cloud.trace_v1.services.trace_service.pagers.ListTracesPager: + The response message for the ListTraces method. Iterating over this object will yield results and resolve additional pages automatically. @@ -319,16 +422,17 @@ def get_trace( r"""Gets a single trace by its ID. Args: - request (:class:`~.trace.GetTraceRequest`): + request (google.cloud.trace_v1.types.GetTraceRequest): The request object. The request message for the `GetTrace` method. - project_id (:class:`str`): + project_id (str): Required. ID of the Cloud project where the trace data is stored. + This corresponds to the ``project_id`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - trace_id (:class:`str`): + trace_id (str): Required. ID of the trace to return. This corresponds to the ``trace_id`` field on the ``request`` instance; if ``request`` is provided, this @@ -341,7 +445,7 @@ def get_trace( sent along with the request as metadata. Returns: - ~.trace.Trace: + google.cloud.trace_v1.types.Trace: A trace describes how long it takes for an application to perform an operation. It consists of a set of @@ -403,16 +507,17 @@ def patch_traces( a new trace is created. Args: - request (:class:`~.trace.PatchTracesRequest`): + request (google.cloud.trace_v1.types.PatchTracesRequest): The request object. The request message for the `PatchTraces` method. - project_id (:class:`str`): + project_id (str): Required. ID of the Cloud project where the trace data is stored. + This corresponds to the ``project_id`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - traces (:class:`~.trace.Traces`): + traces (google.cloud.trace_v1.types.Traces): Required. The body of the message. This corresponds to the ``traces`` field on the ``request`` instance; if ``request`` is provided, this diff --git a/google/cloud/trace_v1/services/trace_service/pagers.py b/google/cloud/trace_v1/services/trace_service/pagers.py index f5b2439b..ad600632 100644 --- a/google/cloud/trace_v1/services/trace_service/pagers.py +++ b/google/cloud/trace_v1/services/trace_service/pagers.py @@ -15,7 +15,16 @@ # limitations under the License. # -from typing import Any, AsyncIterable, Awaitable, Callable, Iterable, Sequence, Tuple +from typing import ( + Any, + AsyncIterable, + Awaitable, + Callable, + Iterable, + Sequence, + Tuple, + Optional, +) from google.cloud.trace_v1.types import trace @@ -24,7 +33,7 @@ class ListTracesPager: """A pager for iterating through ``list_traces`` requests. This class thinly wraps an initial - :class:`~.trace.ListTracesResponse` object, and + :class:`google.cloud.trace_v1.types.ListTracesResponse` object, and provides an ``__iter__`` method to iterate through its ``traces`` field. @@ -33,7 +42,7 @@ class ListTracesPager: through the ``traces`` field on the corresponding responses. - All the usual :class:`~.trace.ListTracesResponse` + All the usual :class:`google.cloud.trace_v1.types.ListTracesResponse` attributes are available on the pager. If multiple requests are made, only the most recent response is retained, and thus used for attribute lookup. """ @@ -51,9 +60,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.trace.ListTracesRequest`): + request (google.cloud.trace_v1.types.ListTracesRequest): The initial request object. - response (:class:`~.trace.ListTracesResponse`): + response (google.cloud.trace_v1.types.ListTracesResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. @@ -86,7 +95,7 @@ class ListTracesAsyncPager: """A pager for iterating through ``list_traces`` requests. This class thinly wraps an initial - :class:`~.trace.ListTracesResponse` object, and + :class:`google.cloud.trace_v1.types.ListTracesResponse` object, and provides an ``__aiter__`` method to iterate through its ``traces`` field. @@ -95,7 +104,7 @@ class ListTracesAsyncPager: through the ``traces`` field on the corresponding responses. - All the usual :class:`~.trace.ListTracesResponse` + All the usual :class:`google.cloud.trace_v1.types.ListTracesResponse` attributes are available on the pager. If multiple requests are made, only the most recent response is retained, and thus used for attribute lookup. """ @@ -113,9 +122,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.trace.ListTracesRequest`): + request (google.cloud.trace_v1.types.ListTracesRequest): The initial request object. - response (:class:`~.trace.ListTracesResponse`): + response (google.cloud.trace_v1.types.ListTracesResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. diff --git a/google/cloud/trace_v1/services/trace_service/transports/__init__.py b/google/cloud/trace_v1/services/trace_service/transports/__init__.py index 134fa0ad..b860866b 100644 --- a/google/cloud/trace_v1/services/trace_service/transports/__init__.py +++ b/google/cloud/trace_v1/services/trace_service/transports/__init__.py @@ -28,7 +28,6 @@ _transport_registry["grpc"] = TraceServiceGrpcTransport _transport_registry["grpc_asyncio"] = TraceServiceGrpcAsyncIOTransport - __all__ = ( "TraceServiceTransport", "TraceServiceGrpcTransport", diff --git a/google/cloud/trace_v1/services/trace_service/transports/base.py b/google/cloud/trace_v1/services/trace_service/transports/base.py index b8b99849..5557e905 100644 --- a/google/cloud/trace_v1/services/trace_service/transports/base.py +++ b/google/cloud/trace_v1/services/trace_service/transports/base.py @@ -19,7 +19,7 @@ import typing import pkg_resources -from google import auth +from google import auth # type: ignore from google.api_core import exceptions # type: ignore from google.api_core import gapic_v1 # type: ignore from google.api_core import retry as retries # type: ignore @@ -72,10 +72,10 @@ def __init__( scope (Optional[Sequence[str]]): A list of scopes. quota_project_id (Optional[str]): An optional project to use for billing and quota. - client_info (google.api_core.gapic_v1.client_info.ClientInfo): - The client info used to send a user-agent string along with - API requests. If ``None``, then default info will be used. - Generally, you only need to set this if you're developing + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing your own client library. """ # Save the hostname. Default to port 443 (HTTPS) if none is specified. @@ -83,6 +83,9 @@ def __init__( host += ":443" self._host = host + # Save the scopes. + self._scopes = scopes or self.AUTH_SCOPES + # If no credentials are provided, then determine the appropriate # defaults. if credentials and credentials_file: @@ -92,20 +95,17 @@ def __init__( if credentials_file is not None: credentials, _ = auth.load_credentials_from_file( - credentials_file, scopes=scopes, quota_project_id=quota_project_id + credentials_file, scopes=self._scopes, quota_project_id=quota_project_id ) elif credentials is None: credentials, _ = auth.default( - scopes=scopes, quota_project_id=quota_project_id + scopes=self._scopes, quota_project_id=quota_project_id ) # Save the credentials. self._credentials = credentials - # Lifted into its own function so it can be stubbed out during tests. - self._prep_wrapped_messages(client_info) - def _prep_wrapped_messages(self, client_info): # Precompute the wrapped methods. self._wrapped_methods = { @@ -118,6 +118,7 @@ def _prep_wrapped_messages(self, client_info): predicate=retries.if_exception_type( exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, ), + deadline=45.0, ), default_timeout=45.0, client_info=client_info, @@ -131,6 +132,7 @@ def _prep_wrapped_messages(self, client_info): predicate=retries.if_exception_type( exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, ), + deadline=45.0, ), default_timeout=45.0, client_info=client_info, @@ -144,6 +146,7 @@ def _prep_wrapped_messages(self, client_info): predicate=retries.if_exception_type( exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, ), + deadline=45.0, ), default_timeout=45.0, client_info=client_info, diff --git a/google/cloud/trace_v1/services/trace_service/transports/grpc.py b/google/cloud/trace_v1/services/trace_service/transports/grpc.py index 998ffea3..f6c1a2dd 100644 --- a/google/cloud/trace_v1/services/trace_service/transports/grpc.py +++ b/google/cloud/trace_v1/services/trace_service/transports/grpc.py @@ -15,6 +15,7 @@ # limitations under the License. # +import warnings from typing import Callable, Dict, Optional, Sequence, Tuple from google.api_core import grpc_helpers # type: ignore @@ -23,7 +24,6 @@ from google.auth import credentials # type: ignore from google.auth.transport.grpc import SslCredentials # type: ignore - import grpc # type: ignore from google.cloud.trace_v1.types import trace @@ -62,6 +62,8 @@ def __init__( channel: grpc.Channel = None, api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -82,20 +84,26 @@ def __init__( ignored if ``channel`` is provided. channel (Optional[grpc.Channel]): A ``Channel`` instance through which to make calls. - api_mtls_endpoint (Optional[str]): The mutual TLS endpoint. If - provided, it overrides the ``host`` argument and tries to create + api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. + If provided, it overrides the ``host`` argument and tries to create a mutual TLS channel with client SSL credentials from ``client_cert_source`` or applicatin default SSL credentials. - client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): A - callback to provide client SSL certificate bytes and private key - bytes, both in PEM format. It is ignored if ``api_mtls_endpoint`` - is None. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): + Deprecated. A callback to provide client SSL certificate bytes and + private key bytes, both in PEM format. It is ignored if + ``api_mtls_endpoint`` is None. + ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials + for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. - client_info (google.api_core.gapic_v1.client_info.ClientInfo): - The client info used to send a user-agent string along with - API requests. If ``None``, then default info will be used. - Generally, you only need to set this if you're developing + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing your own client library. Raises: @@ -104,57 +112,70 @@ def __init__( google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` and ``credentials_file`` are passed. """ + self._grpc_channel = None + self._ssl_channel_credentials = ssl_channel_credentials + self._stubs: Dict[str, Callable] = {} + + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: - # Sanity check: Ensure that channel and credentials are not both - # provided. + # Ignore credentials if a channel was passed. credentials = False - # If a channel was explicitly provided, set it. self._grpc_channel = channel - elif api_mtls_endpoint: - host = ( - api_mtls_endpoint - if ":" in api_mtls_endpoint - else api_mtls_endpoint + ":443" - ) + self._ssl_channel_credentials = None + + else: + if api_mtls_endpoint: + host = api_mtls_endpoint + + # Create SSL credentials with client_cert_source or application + # default SSL credentials. + if client_cert_source: + cert, key = client_cert_source() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + else: + self._ssl_channel_credentials = SslCredentials().ssl_credentials - if credentials is None: - credentials, _ = auth.default( - scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id - ) - - # Create SSL credentials with client_cert_source or application - # default SSL credentials. - if client_cert_source: - cert, key = client_cert_source() - ssl_credentials = grpc.ssl_channel_credentials( - certificate_chain=cert, private_key=key - ) else: - ssl_credentials = SslCredentials().ssl_credentials - - # create a new channel. The provided one is ignored. - self._grpc_channel = type(self).create_channel( - host, - credentials=credentials, - credentials_file=credentials_file, - ssl_credentials=ssl_credentials, - scopes=scopes or self.AUTH_SCOPES, - quota_project_id=quota_project_id, - ) - - self._stubs = {} # type: Dict[str, Callable] + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) - # Run the base constructor. + # The base transport sets the host, credentials and scopes super().__init__( host=host, credentials=credentials, credentials_file=credentials_file, - scopes=scopes or self.AUTH_SCOPES, + scopes=scopes, quota_project_id=quota_project_id, client_info=client_info, ) + if not self._grpc_channel: + self._grpc_channel = type(self).create_channel( + self._host, + credentials=self._credentials, + credentials_file=credentials_file, + scopes=self._scopes, + ssl_credentials=self._ssl_channel_credentials, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Wrap messages. This must be done after self._grpc_channel exists + self._prep_wrapped_messages(client_info) + @classmethod def create_channel( cls, @@ -167,7 +188,7 @@ def create_channel( ) -> grpc.Channel: """Create and return a gRPC channel object. Args: - address (Optionsl[str]): The host for the channel to use. + host (Optional[str]): The host for the channel to use. credentials (Optional[~.Credentials]): The authorization credentials to attach to requests. These credentials identify this application to the service. If @@ -202,19 +223,8 @@ def create_channel( @property def grpc_channel(self) -> grpc.Channel: - """Create the channel designed to connect to this service. - - This property caches on the instance; repeated calls return - the same channel. + """Return the channel designed to connect to this service. """ - # Sanity check: Only create a new channel if we do not already - # have one. - if not hasattr(self, "_grpc_channel"): - self._grpc_channel = self.create_channel( - self._host, credentials=self._credentials, - ) - - # Return the channel from cache. return self._grpc_channel @property diff --git a/google/cloud/trace_v1/services/trace_service/transports/grpc_asyncio.py b/google/cloud/trace_v1/services/trace_service/transports/grpc_asyncio.py index 7ebc87d4..48963ead 100644 --- a/google/cloud/trace_v1/services/trace_service/transports/grpc_asyncio.py +++ b/google/cloud/trace_v1/services/trace_service/transports/grpc_asyncio.py @@ -15,10 +15,12 @@ # limitations under the License. # +import warnings from typing import Awaitable, Callable, Dict, Optional, Sequence, Tuple from google.api_core import gapic_v1 # type: ignore from google.api_core import grpc_helpers_async # type: ignore +from google import auth # type: ignore from google.auth import credentials # type: ignore from google.auth.transport.grpc import SslCredentials # type: ignore @@ -65,7 +67,7 @@ def create_channel( ) -> aio.Channel: """Create and return a gRPC AsyncIO channel object. Args: - address (Optional[str]): The host for the channel to use. + host (Optional[str]): The host for the channel to use. credentials (Optional[~.Credentials]): The authorization credentials to attach to requests. These credentials identify this application to the service. If @@ -104,6 +106,8 @@ def __init__( channel: aio.Channel = None, api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id=None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -125,20 +129,26 @@ def __init__( are passed to :func:`google.auth.default`. channel (Optional[aio.Channel]): A ``Channel`` instance through which to make calls. - api_mtls_endpoint (Optional[str]): The mutual TLS endpoint. If - provided, it overrides the ``host`` argument and tries to create + api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. + If provided, it overrides the ``host`` argument and tries to create a mutual TLS channel with client SSL credentials from ``client_cert_source`` or applicatin default SSL credentials. - client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): A - callback to provide client SSL certificate bytes and private key - bytes, both in PEM format. It is ignored if ``api_mtls_endpoint`` - is None. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): + Deprecated. A callback to provide client SSL certificate bytes and + private key bytes, both in PEM format. It is ignored if + ``api_mtls_endpoint`` is None. + ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials + for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. - client_info (google.api_core.gapic_v1.client_info.ClientInfo): - The client info used to send a user-agent string along with - API requests. If ``None``, then default info will be used. - Generally, you only need to set this if you're developing + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing your own client library. Raises: @@ -147,51 +157,69 @@ def __init__( google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` and ``credentials_file`` are passed. """ + self._grpc_channel = None + self._ssl_channel_credentials = ssl_channel_credentials + self._stubs: Dict[str, Callable] = {} + + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: - # Sanity check: Ensure that channel and credentials are not both - # provided. + # Ignore credentials if a channel was passed. credentials = False - # If a channel was explicitly provided, set it. self._grpc_channel = channel - elif api_mtls_endpoint: - host = ( - api_mtls_endpoint - if ":" in api_mtls_endpoint - else api_mtls_endpoint + ":443" - ) + self._ssl_channel_credentials = None + + else: + if api_mtls_endpoint: + host = api_mtls_endpoint + + # Create SSL credentials with client_cert_source or application + # default SSL credentials. + if client_cert_source: + cert, key = client_cert_source() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + else: + self._ssl_channel_credentials = SslCredentials().ssl_credentials - # Create SSL credentials with client_cert_source or application - # default SSL credentials. - if client_cert_source: - cert, key = client_cert_source() - ssl_credentials = grpc.ssl_channel_credentials( - certificate_chain=cert, private_key=key - ) else: - ssl_credentials = SslCredentials().ssl_credentials - - # create a new channel. The provided one is ignored. - self._grpc_channel = type(self).create_channel( - host, - credentials=credentials, - credentials_file=credentials_file, - ssl_credentials=ssl_credentials, - scopes=scopes or self.AUTH_SCOPES, - quota_project_id=quota_project_id, - ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) - # Run the base constructor. + # The base transport sets the host, credentials and scopes super().__init__( host=host, credentials=credentials, credentials_file=credentials_file, - scopes=scopes or self.AUTH_SCOPES, + scopes=scopes, quota_project_id=quota_project_id, client_info=client_info, ) - self._stubs = {} + if not self._grpc_channel: + self._grpc_channel = type(self).create_channel( + self._host, + credentials=self._credentials, + credentials_file=credentials_file, + scopes=self._scopes, + ssl_credentials=self._ssl_channel_credentials, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Wrap messages. This must be done after self._grpc_channel exists + self._prep_wrapped_messages(client_info) @property def grpc_channel(self) -> aio.Channel: @@ -200,13 +228,6 @@ def grpc_channel(self) -> aio.Channel: This property caches on the instance; repeated calls return the same channel. """ - # Sanity check: Only create a new channel if we do not already - # have one. - if not hasattr(self, "_grpc_channel"): - self._grpc_channel = self.create_channel( - self._host, credentials=self._credentials, - ) - # Return the channel from cache. return self._grpc_channel diff --git a/google/cloud/trace_v1/types/__init__.py b/google/cloud/trace_v1/types/__init__.py index 66dbf851..540e3e00 100644 --- a/google/cloud/trace_v1/types/__init__.py +++ b/google/cloud/trace_v1/types/__init__.py @@ -16,22 +16,21 @@ # from .trace import ( - Trace, - Traces, - TraceSpan, + GetTraceRequest, ListTracesRequest, ListTracesResponse, - GetTraceRequest, PatchTracesRequest, + Trace, + Traces, + TraceSpan, ) - __all__ = ( - "Trace", - "Traces", - "TraceSpan", + "GetTraceRequest", "ListTracesRequest", "ListTracesResponse", - "GetTraceRequest", "PatchTracesRequest", + "Trace", + "Traces", + "TraceSpan", ) diff --git a/google/cloud/trace_v1/types/trace.py b/google/cloud/trace_v1/types/trace.py index 81cfbc93..3e75d906 100644 --- a/google/cloud/trace_v1/types/trace.py +++ b/google/cloud/trace_v1/types/trace.py @@ -48,7 +48,7 @@ class Trace(proto.Message): Globally unique identifier for the trace. This identifier is a 128-bit numeric value formatted as a 32-byte hex string. For example, ``382d4f4c6b7bb2f4a972559d9085001d``. - spans (Sequence[~.trace.TraceSpan]): + spans (Sequence[google.cloud.trace_v1.types.TraceSpan]): Collection of spans in the trace. """ @@ -63,11 +63,11 @@ class Traces(proto.Message): r"""List of new or updated traces. Attributes: - traces (Sequence[~.trace.Trace]): + traces (Sequence[google.cloud.trace_v1.types.Trace]): List of traces. """ - traces = proto.RepeatedField(proto.MESSAGE, number=1, message=Trace,) + traces = proto.RepeatedField(proto.MESSAGE, number=1, message="Trace",) class TraceSpan(proto.Message): @@ -83,7 +83,7 @@ class TraceSpan(proto.Message): Identifier for the span. Must be a 64-bit integer other than 0 and unique within a trace. For example, ``2205310701640571284``. - kind (~.trace.TraceSpan.SpanKind): + kind (google.cloud.trace_v1.types.TraceSpan.SpanKind): Distinguishes between spans generated in a particular context. For example, two spans with the same name may be distinguished using ``RPC_CLIENT`` and ``RPC_SERVER`` to @@ -98,15 +98,15 @@ class TraceSpan(proto.Message): same call point, a best practice is to use a consistent name, which makes it easier to correlate cross-trace spans. - start_time (~.timestamp.Timestamp): + start_time (google.protobuf.timestamp_pb2.Timestamp): Start time of the span in nanoseconds from the UNIX epoch. - end_time (~.timestamp.Timestamp): + end_time (google.protobuf.timestamp_pb2.Timestamp): End time of the span in nanoseconds from the UNIX epoch. parent_span_id (int): Optional. ID of the parent span, if any. - labels (Sequence[~.trace.TraceSpan.LabelsEntry]): + labels (Sequence[google.cloud.trace_v1.types.TraceSpan.LabelsEntry]): Collection of labels associated with the span. Label keys must be less than 128 bytes. Label values must be less than 16 kilobytes (10MB for ``/stacktrace`` values). @@ -176,7 +176,7 @@ class ListTracesRequest(proto.Message): project_id (str): Required. ID of the Cloud project where the trace data is stored. - view (~.trace.ListTracesRequest.ViewType): + view (google.cloud.trace_v1.types.ListTracesRequest.ViewType): Optional. Type of data returned for traces in the list. Default is ``MINIMAL``. page_size (int): @@ -189,11 +189,11 @@ class ListTracesRequest(proto.Message): Token identifying the page of results to return. If provided, use the value of the ``next_page_token`` field from a previous request. - start_time (~.timestamp.Timestamp): + start_time (google.protobuf.timestamp_pb2.Timestamp): Start of the time interval (inclusive) during which the trace data was collected from the application. - end_time (~.timestamp.Timestamp): + end_time (google.protobuf.timestamp_pb2.Timestamp): End of the time interval (inclusive) during which the trace data was collected from the application. @@ -275,7 +275,7 @@ class ListTracesResponse(proto.Message): r"""The response message for the ``ListTraces`` method. Attributes: - traces (Sequence[~.trace.Trace]): + traces (Sequence[google.cloud.trace_v1.types.Trace]): List of trace records as specified by the view parameter. next_page_token (str): @@ -289,7 +289,7 @@ class ListTracesResponse(proto.Message): def raw_page(self): return self - traces = proto.RepeatedField(proto.MESSAGE, number=1, message=Trace,) + traces = proto.RepeatedField(proto.MESSAGE, number=1, message="Trace",) next_page_token = proto.Field(proto.STRING, number=2) @@ -317,13 +317,13 @@ class PatchTracesRequest(proto.Message): project_id (str): Required. ID of the Cloud project where the trace data is stored. - traces (~.trace.Traces): + traces (google.cloud.trace_v1.types.Traces): Required. The body of the message. """ project_id = proto.Field(proto.STRING, number=1) - traces = proto.Field(proto.MESSAGE, number=2, message=Traces,) + traces = proto.Field(proto.MESSAGE, number=2, message="Traces",) __all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/devtools/cloudtrace_v2/__init__.py b/google/cloud/trace_v2/__init__.py similarity index 100% rename from google/devtools/cloudtrace_v2/__init__.py rename to google/cloud/trace_v2/__init__.py diff --git a/google/cloud/trace_v2/services/trace_service/async_client.py b/google/cloud/trace_v2/services/trace_service/async_client.py index a400f60a..16df441a 100644 --- a/google/cloud/trace_v2/services/trace_service/async_client.py +++ b/google/cloud/trace_v2/services/trace_service/async_client.py @@ -54,10 +54,74 @@ class TraceServiceAsyncClient: DEFAULT_MTLS_ENDPOINT = TraceServiceClient.DEFAULT_MTLS_ENDPOINT span_path = staticmethod(TraceServiceClient.span_path) + parse_span_path = staticmethod(TraceServiceClient.parse_span_path) + + common_billing_account_path = staticmethod( + TraceServiceClient.common_billing_account_path + ) + parse_common_billing_account_path = staticmethod( + TraceServiceClient.parse_common_billing_account_path + ) + + common_folder_path = staticmethod(TraceServiceClient.common_folder_path) + parse_common_folder_path = staticmethod(TraceServiceClient.parse_common_folder_path) + + common_organization_path = staticmethod(TraceServiceClient.common_organization_path) + parse_common_organization_path = staticmethod( + TraceServiceClient.parse_common_organization_path + ) + + common_project_path = staticmethod(TraceServiceClient.common_project_path) + parse_common_project_path = staticmethod( + TraceServiceClient.parse_common_project_path + ) + + common_location_path = staticmethod(TraceServiceClient.common_location_path) + parse_common_location_path = staticmethod( + TraceServiceClient.parse_common_location_path + ) + + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + TraceServiceAsyncClient: The constructed client. + """ + return TraceServiceClient.from_service_account_info.__func__(TraceServiceAsyncClient, info, *args, **kwargs) # type: ignore + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + TraceServiceAsyncClient: The constructed client. + """ + return TraceServiceClient.from_service_account_file.__func__(TraceServiceAsyncClient, filename, *args, **kwargs) # type: ignore - from_service_account_file = TraceServiceClient.from_service_account_file from_service_account_json = from_service_account_file + @property + def transport(self) -> TraceServiceTransport: + """Return the transport used by the client instance. + + Returns: + TraceServiceTransport: The transport used by the client instance. + """ + return self._client.transport + get_transport_class = functools.partial( type(TraceServiceClient).get_transport_class, type(TraceServiceClient) ) @@ -84,16 +148,19 @@ def __init__( client_options (ClientOptions): Custom options for the client. It won't take effect if a ``transport`` instance is provided. (1) The ``api_endpoint`` property can be used to override the - default endpoint provided by the client. GOOGLE_API_USE_MTLS + default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT environment variable can also be used to override the endpoint: "always" (always use the default mTLS endpoint), "never" (always - use the default regular endpoint, this is the default value for - the environment variable) and "auto" (auto switch to the default - mTLS endpoint if client SSL credentials is present). However, - the ``api_endpoint`` property takes precedence if provided. - (2) The ``client_cert_source`` property is used to provide client - SSL credentials for mutual TLS transport. If not provided, the - default SSL credentials will be used if present. + use the default regular endpoint) and "auto" (auto switch to the + default mTLS endpoint if client certificate is present, this is + the default value). However, the ``api_endpoint`` property takes + precedence if provided. + (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable + is "true", then the ``client_cert_source`` property can be used + to provide client certificate for mutual TLS transport. If + not provided, the default SSL client certificate will be used if + present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not + set, no client certificate will be used. Raises: google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport @@ -121,19 +188,21 @@ async def batch_write_spans( update existing spans. Args: - request (:class:`~.tracing.BatchWriteSpansRequest`): + request (:class:`google.cloud.trace_v2.types.BatchWriteSpansRequest`): The request object. The request message for the `BatchWriteSpans` method. name (:class:`str`): Required. The name of the project where the spans belong. The format is ``projects/[PROJECT_ID]``. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - spans (:class:`Sequence[~.trace.Span]`): + spans (:class:`Sequence[google.cloud.trace_v2.types.Span]`): Required. A list of new spans. The span names must not match existing spans, or the results are undefined. + This corresponds to the ``spans`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -147,7 +216,8 @@ async def batch_write_spans( # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - if request is not None and any([name, spans]): + has_flattened_params = any([name, spans]) + if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " "the individual field arguments should be set." @@ -160,8 +230,9 @@ async def batch_write_spans( if name is not None: request.name = name - if spans is not None: - request.spans = spans + + if spans: + request.spans.extend(spans) # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. @@ -193,7 +264,7 @@ async def create_span( r"""Creates a new span. Args: - request (:class:`~.trace.Span`): + request (:class:`google.cloud.trace_v2.types.Span`): The request object. A span represents a single operation within a trace. Spans can be nested to form a trace tree. Often, a trace contains a root span that describes @@ -210,7 +281,7 @@ async def create_span( sent along with the request as metadata. Returns: - ~.trace.Span: + google.cloud.trace_v2.types.Span: A span represents a single operation within a trace. Spans can be nested to form a trace tree. Often, a trace @@ -238,6 +309,7 @@ async def create_span( predicate=retries.if_exception_type( exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, ), + deadline=120.0, ), default_timeout=120.0, client_info=DEFAULT_CLIENT_INFO, diff --git a/google/cloud/trace_v2/services/trace_service/client.py b/google/cloud/trace_v2/services/trace_service/client.py index 816ad9d3..19758f21 100644 --- a/google/cloud/trace_v2/services/trace_service/client.py +++ b/google/cloud/trace_v2/services/trace_service/client.py @@ -16,17 +16,19 @@ # from collections import OrderedDict +from distutils import util import os import re -from typing import Callable, Dict, Sequence, Tuple, Type, Union +from typing import Callable, Dict, Optional, Sequence, Tuple, Type, Union import pkg_resources -import google.api_core.client_options as ClientOptions # type: ignore +from google.api_core import client_options as client_options_lib # type: ignore from google.api_core import exceptions # type: ignore from google.api_core import gapic_v1 # type: ignore from google.api_core import retry as retries # type: ignore from google.auth import credentials # type: ignore from google.auth.transport import mtls # type: ignore +from google.auth.transport.grpc import SslCredentials # type: ignore from google.auth.exceptions import MutualTLSChannelError # type: ignore from google.oauth2 import service_account # type: ignore @@ -115,6 +117,22 @@ def _get_default_mtls_endpoint(api_endpoint): DEFAULT_ENDPOINT ) + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + TraceServiceClient: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_info(info) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + @classmethod def from_service_account_file(cls, filename: str, *args, **kwargs): """Creates an instance of this client using the provided credentials @@ -127,7 +145,7 @@ def from_service_account_file(cls, filename: str, *args, **kwargs): kwargs: Additional arguments to pass to the constructor. Returns: - {@api.name}: The constructed client. + TraceServiceClient: The constructed client. """ credentials = service_account.Credentials.from_service_account_file(filename) kwargs["credentials"] = credentials @@ -135,6 +153,15 @@ def from_service_account_file(cls, filename: str, *args, **kwargs): from_service_account_json = from_service_account_file + @property + def transport(self) -> TraceServiceTransport: + """Return the transport used by the client instance. + + Returns: + TraceServiceTransport: The transport used by the client instance. + """ + return self._transport + @staticmethod def span_path(project: str, trace: str, span: str,) -> str: """Return a fully-qualified span string.""" @@ -151,12 +178,71 @@ def parse_span_path(path: str) -> Dict[str, str]: ) return m.groupdict() if m else {} + @staticmethod + def common_billing_account_path(billing_account: str,) -> str: + """Return a fully-qualified billing_account string.""" + return "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + + @staticmethod + def parse_common_billing_account_path(path: str) -> Dict[str, str]: + """Parse a billing_account path into its component segments.""" + m = re.match(r"^billingAccounts/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_folder_path(folder: str,) -> str: + """Return a fully-qualified folder string.""" + return "folders/{folder}".format(folder=folder,) + + @staticmethod + def parse_common_folder_path(path: str) -> Dict[str, str]: + """Parse a folder path into its component segments.""" + m = re.match(r"^folders/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_organization_path(organization: str,) -> str: + """Return a fully-qualified organization string.""" + return "organizations/{organization}".format(organization=organization,) + + @staticmethod + def parse_common_organization_path(path: str) -> Dict[str, str]: + """Parse a organization path into its component segments.""" + m = re.match(r"^organizations/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_project_path(project: str,) -> str: + """Return a fully-qualified project string.""" + return "projects/{project}".format(project=project,) + + @staticmethod + def parse_common_project_path(path: str) -> Dict[str, str]: + """Parse a project path into its component segments.""" + m = re.match(r"^projects/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_location_path(project: str, location: str,) -> str: + """Return a fully-qualified location string.""" + return "projects/{project}/locations/{location}".format( + project=project, location=location, + ) + + @staticmethod + def parse_common_location_path(path: str) -> Dict[str, str]: + """Parse a location path into its component segments.""" + m = re.match(r"^projects/(?P.+?)/locations/(?P.+?)$", path) + return m.groupdict() if m else {} + def __init__( self, *, - credentials: credentials.Credentials = None, - transport: Union[str, TraceServiceTransport] = None, - client_options: ClientOptions = None, + credentials: Optional[credentials.Credentials] = None, + transport: Union[str, TraceServiceTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: """Instantiate the trace service client. @@ -167,26 +253,29 @@ def __init__( credentials identify the application to the service; if none are specified, the client will attempt to ascertain the credentials from the environment. - transport (Union[str, ~.TraceServiceTransport]): The + transport (Union[str, TraceServiceTransport]): The transport to use. If set to None, a transport is chosen automatically. - client_options (ClientOptions): Custom options for the client. It - won't take effect if a ``transport`` instance is provided. + client_options (google.api_core.client_options.ClientOptions): Custom options for the + client. It won't take effect if a ``transport`` instance is provided. (1) The ``api_endpoint`` property can be used to override the - default endpoint provided by the client. GOOGLE_API_USE_MTLS + default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT environment variable can also be used to override the endpoint: "always" (always use the default mTLS endpoint), "never" (always - use the default regular endpoint, this is the default value for - the environment variable) and "auto" (auto switch to the default - mTLS endpoint if client SSL credentials is present). However, - the ``api_endpoint`` property takes precedence if provided. - (2) The ``client_cert_source`` property is used to provide client - SSL credentials for mutual TLS transport. If not provided, the - default SSL credentials will be used if present. - client_info (google.api_core.gapic_v1.client_info.ClientInfo): - The client info used to send a user-agent string along with - API requests. If ``None``, then default info will be used. - Generally, you only need to set this if you're developing + use the default regular endpoint) and "auto" (auto switch to the + default mTLS endpoint if client certificate is present, this is + the default value). However, the ``api_endpoint`` property takes + precedence if provided. + (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable + is "true", then the ``client_cert_source`` property can be used + to provide client certificate for mutual TLS transport. If + not provided, the default SSL client certificate will be used if + present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not + set, no client certificate will be used. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing your own client library. Raises: @@ -194,29 +283,43 @@ def __init__( creation failed for any reason. """ if isinstance(client_options, dict): - client_options = ClientOptions.from_dict(client_options) + client_options = client_options_lib.from_dict(client_options) if client_options is None: - client_options = ClientOptions.ClientOptions() + client_options = client_options_lib.ClientOptions() + + # Create SSL credentials for mutual TLS if needed. + use_client_cert = bool( + util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) + ) + + client_cert_source_func = None + is_mtls = False + if use_client_cert: + if client_options.client_cert_source: + is_mtls = True + client_cert_source_func = client_options.client_cert_source + else: + is_mtls = mtls.has_default_client_cert_source() + client_cert_source_func = ( + mtls.default_client_cert_source() if is_mtls else None + ) - if client_options.api_endpoint is None: - use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS", "never") + # Figure out which api endpoint to use. + if client_options.api_endpoint is not None: + api_endpoint = client_options.api_endpoint + else: + use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto") if use_mtls_env == "never": - client_options.api_endpoint = self.DEFAULT_ENDPOINT + api_endpoint = self.DEFAULT_ENDPOINT elif use_mtls_env == "always": - client_options.api_endpoint = self.DEFAULT_MTLS_ENDPOINT + api_endpoint = self.DEFAULT_MTLS_ENDPOINT elif use_mtls_env == "auto": - has_client_cert_source = ( - client_options.client_cert_source is not None - or mtls.has_default_client_cert_source() - ) - client_options.api_endpoint = ( - self.DEFAULT_MTLS_ENDPOINT - if has_client_cert_source - else self.DEFAULT_ENDPOINT + api_endpoint = ( + self.DEFAULT_MTLS_ENDPOINT if is_mtls else self.DEFAULT_ENDPOINT ) else: raise MutualTLSChannelError( - "Unsupported GOOGLE_API_USE_MTLS value. Accepted values: never, auto, always" + "Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted values: never, auto, always" ) # Save or instantiate the transport. @@ -240,10 +343,9 @@ def __init__( self._transport = Transport( credentials=credentials, credentials_file=client_options.credentials_file, - host=client_options.api_endpoint, + host=api_endpoint, scopes=client_options.scopes, - api_mtls_endpoint=client_options.api_endpoint, - client_cert_source=client_options.client_cert_source, + client_cert_source_for_mtls=client_cert_source_func, quota_project_id=client_options.quota_project_id, client_info=client_info, ) @@ -262,19 +364,21 @@ def batch_write_spans( update existing spans. Args: - request (:class:`~.tracing.BatchWriteSpansRequest`): + request (google.cloud.trace_v2.types.BatchWriteSpansRequest): The request object. The request message for the `BatchWriteSpans` method. - name (:class:`str`): + name (str): Required. The name of the project where the spans belong. The format is ``projects/[PROJECT_ID]``. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - spans (:class:`Sequence[~.trace.Span]`): + spans (Sequence[google.cloud.trace_v2.types.Span]): Required. A list of new spans. The span names must not match existing spans, or the results are undefined. + This corresponds to the ``spans`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -336,7 +440,7 @@ def create_span( r"""Creates a new span. Args: - request (:class:`~.trace.Span`): + request (google.cloud.trace_v2.types.Span): The request object. A span represents a single operation within a trace. Spans can be nested to form a trace tree. Often, a trace contains a root span that describes @@ -353,7 +457,7 @@ def create_span( sent along with the request as metadata. Returns: - ~.trace.Span: + google.cloud.trace_v2.types.Span: A span represents a single operation within a trace. Spans can be nested to form a trace tree. Often, a trace diff --git a/google/cloud/trace_v2/services/trace_service/transports/__init__.py b/google/cloud/trace_v2/services/trace_service/transports/__init__.py index 134fa0ad..b860866b 100644 --- a/google/cloud/trace_v2/services/trace_service/transports/__init__.py +++ b/google/cloud/trace_v2/services/trace_service/transports/__init__.py @@ -28,7 +28,6 @@ _transport_registry["grpc"] = TraceServiceGrpcTransport _transport_registry["grpc_asyncio"] = TraceServiceGrpcAsyncIOTransport - __all__ = ( "TraceServiceTransport", "TraceServiceGrpcTransport", diff --git a/google/cloud/trace_v2/services/trace_service/transports/base.py b/google/cloud/trace_v2/services/trace_service/transports/base.py index a271eea7..43951d62 100644 --- a/google/cloud/trace_v2/services/trace_service/transports/base.py +++ b/google/cloud/trace_v2/services/trace_service/transports/base.py @@ -19,7 +19,7 @@ import typing import pkg_resources -from google import auth +from google import auth # type: ignore from google.api_core import exceptions # type: ignore from google.api_core import gapic_v1 # type: ignore from google.api_core import retry as retries # type: ignore @@ -72,10 +72,10 @@ def __init__( scope (Optional[Sequence[str]]): A list of scopes. quota_project_id (Optional[str]): An optional project to use for billing and quota. - client_info (google.api_core.gapic_v1.client_info.ClientInfo): - The client info used to send a user-agent string along with - API requests. If ``None``, then default info will be used. - Generally, you only need to set this if you're developing + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing your own client library. """ # Save the hostname. Default to port 443 (HTTPS) if none is specified. @@ -83,6 +83,9 @@ def __init__( host += ":443" self._host = host + # Save the scopes. + self._scopes = scopes or self.AUTH_SCOPES + # If no credentials are provided, then determine the appropriate # defaults. if credentials and credentials_file: @@ -92,20 +95,17 @@ def __init__( if credentials_file is not None: credentials, _ = auth.load_credentials_from_file( - credentials_file, scopes=scopes, quota_project_id=quota_project_id + credentials_file, scopes=self._scopes, quota_project_id=quota_project_id ) elif credentials is None: credentials, _ = auth.default( - scopes=scopes, quota_project_id=quota_project_id + scopes=self._scopes, quota_project_id=quota_project_id ) # Save the credentials. self._credentials = credentials - # Lifted into its own function so it can be stubbed out during tests. - self._prep_wrapped_messages(client_info) - def _prep_wrapped_messages(self, client_info): # Precompute the wrapped methods. self._wrapped_methods = { @@ -121,6 +121,7 @@ def _prep_wrapped_messages(self, client_info): predicate=retries.if_exception_type( exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, ), + deadline=120.0, ), default_timeout=120.0, client_info=client_info, diff --git a/google/cloud/trace_v2/services/trace_service/transports/grpc.py b/google/cloud/trace_v2/services/trace_service/transports/grpc.py index 3bd20e4f..492b9562 100644 --- a/google/cloud/trace_v2/services/trace_service/transports/grpc.py +++ b/google/cloud/trace_v2/services/trace_service/transports/grpc.py @@ -15,6 +15,7 @@ # limitations under the License. # +import warnings from typing import Callable, Dict, Optional, Sequence, Tuple from google.api_core import grpc_helpers # type: ignore @@ -23,7 +24,6 @@ from google.auth import credentials # type: ignore from google.auth.transport.grpc import SslCredentials # type: ignore - import grpc # type: ignore from google.cloud.trace_v2.types import trace @@ -63,6 +63,8 @@ def __init__( channel: grpc.Channel = None, api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -83,20 +85,26 @@ def __init__( ignored if ``channel`` is provided. channel (Optional[grpc.Channel]): A ``Channel`` instance through which to make calls. - api_mtls_endpoint (Optional[str]): The mutual TLS endpoint. If - provided, it overrides the ``host`` argument and tries to create + api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. + If provided, it overrides the ``host`` argument and tries to create a mutual TLS channel with client SSL credentials from ``client_cert_source`` or applicatin default SSL credentials. - client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): A - callback to provide client SSL certificate bytes and private key - bytes, both in PEM format. It is ignored if ``api_mtls_endpoint`` - is None. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): + Deprecated. A callback to provide client SSL certificate bytes and + private key bytes, both in PEM format. It is ignored if + ``api_mtls_endpoint`` is None. + ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials + for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. - client_info (google.api_core.gapic_v1.client_info.ClientInfo): - The client info used to send a user-agent string along with - API requests. If ``None``, then default info will be used. - Generally, you only need to set this if you're developing + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing your own client library. Raises: @@ -105,57 +113,70 @@ def __init__( google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` and ``credentials_file`` are passed. """ + self._grpc_channel = None + self._ssl_channel_credentials = ssl_channel_credentials + self._stubs: Dict[str, Callable] = {} + + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: - # Sanity check: Ensure that channel and credentials are not both - # provided. + # Ignore credentials if a channel was passed. credentials = False - # If a channel was explicitly provided, set it. self._grpc_channel = channel - elif api_mtls_endpoint: - host = ( - api_mtls_endpoint - if ":" in api_mtls_endpoint - else api_mtls_endpoint + ":443" - ) + self._ssl_channel_credentials = None + + else: + if api_mtls_endpoint: + host = api_mtls_endpoint + + # Create SSL credentials with client_cert_source or application + # default SSL credentials. + if client_cert_source: + cert, key = client_cert_source() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + else: + self._ssl_channel_credentials = SslCredentials().ssl_credentials - if credentials is None: - credentials, _ = auth.default( - scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id - ) - - # Create SSL credentials with client_cert_source or application - # default SSL credentials. - if client_cert_source: - cert, key = client_cert_source() - ssl_credentials = grpc.ssl_channel_credentials( - certificate_chain=cert, private_key=key - ) else: - ssl_credentials = SslCredentials().ssl_credentials - - # create a new channel. The provided one is ignored. - self._grpc_channel = type(self).create_channel( - host, - credentials=credentials, - credentials_file=credentials_file, - ssl_credentials=ssl_credentials, - scopes=scopes or self.AUTH_SCOPES, - quota_project_id=quota_project_id, - ) - - self._stubs = {} # type: Dict[str, Callable] + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) - # Run the base constructor. + # The base transport sets the host, credentials and scopes super().__init__( host=host, credentials=credentials, credentials_file=credentials_file, - scopes=scopes or self.AUTH_SCOPES, + scopes=scopes, quota_project_id=quota_project_id, client_info=client_info, ) + if not self._grpc_channel: + self._grpc_channel = type(self).create_channel( + self._host, + credentials=self._credentials, + credentials_file=credentials_file, + scopes=self._scopes, + ssl_credentials=self._ssl_channel_credentials, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Wrap messages. This must be done after self._grpc_channel exists + self._prep_wrapped_messages(client_info) + @classmethod def create_channel( cls, @@ -168,7 +189,7 @@ def create_channel( ) -> grpc.Channel: """Create and return a gRPC channel object. Args: - address (Optionsl[str]): The host for the channel to use. + host (Optional[str]): The host for the channel to use. credentials (Optional[~.Credentials]): The authorization credentials to attach to requests. These credentials identify this application to the service. If @@ -203,19 +224,8 @@ def create_channel( @property def grpc_channel(self) -> grpc.Channel: - """Create the channel designed to connect to this service. - - This property caches on the instance; repeated calls return - the same channel. + """Return the channel designed to connect to this service. """ - # Sanity check: Only create a new channel if we do not already - # have one. - if not hasattr(self, "_grpc_channel"): - self._grpc_channel = self.create_channel( - self._host, credentials=self._credentials, - ) - - # Return the channel from cache. return self._grpc_channel @property diff --git a/google/cloud/trace_v2/services/trace_service/transports/grpc_asyncio.py b/google/cloud/trace_v2/services/trace_service/transports/grpc_asyncio.py index d40f19ee..b19e6cf1 100644 --- a/google/cloud/trace_v2/services/trace_service/transports/grpc_asyncio.py +++ b/google/cloud/trace_v2/services/trace_service/transports/grpc_asyncio.py @@ -15,10 +15,12 @@ # limitations under the License. # +import warnings from typing import Awaitable, Callable, Dict, Optional, Sequence, Tuple from google.api_core import gapic_v1 # type: ignore from google.api_core import grpc_helpers_async # type: ignore +from google import auth # type: ignore from google.auth import credentials # type: ignore from google.auth.transport.grpc import SslCredentials # type: ignore @@ -66,7 +68,7 @@ def create_channel( ) -> aio.Channel: """Create and return a gRPC AsyncIO channel object. Args: - address (Optional[str]): The host for the channel to use. + host (Optional[str]): The host for the channel to use. credentials (Optional[~.Credentials]): The authorization credentials to attach to requests. These credentials identify this application to the service. If @@ -105,6 +107,8 @@ def __init__( channel: aio.Channel = None, api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id=None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -126,20 +130,26 @@ def __init__( are passed to :func:`google.auth.default`. channel (Optional[aio.Channel]): A ``Channel`` instance through which to make calls. - api_mtls_endpoint (Optional[str]): The mutual TLS endpoint. If - provided, it overrides the ``host`` argument and tries to create + api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. + If provided, it overrides the ``host`` argument and tries to create a mutual TLS channel with client SSL credentials from ``client_cert_source`` or applicatin default SSL credentials. - client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): A - callback to provide client SSL certificate bytes and private key - bytes, both in PEM format. It is ignored if ``api_mtls_endpoint`` - is None. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): + Deprecated. A callback to provide client SSL certificate bytes and + private key bytes, both in PEM format. It is ignored if + ``api_mtls_endpoint`` is None. + ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials + for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. - client_info (google.api_core.gapic_v1.client_info.ClientInfo): - The client info used to send a user-agent string along with - API requests. If ``None``, then default info will be used. - Generally, you only need to set this if you're developing + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing your own client library. Raises: @@ -148,51 +158,69 @@ def __init__( google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` and ``credentials_file`` are passed. """ + self._grpc_channel = None + self._ssl_channel_credentials = ssl_channel_credentials + self._stubs: Dict[str, Callable] = {} + + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: - # Sanity check: Ensure that channel and credentials are not both - # provided. + # Ignore credentials if a channel was passed. credentials = False - # If a channel was explicitly provided, set it. self._grpc_channel = channel - elif api_mtls_endpoint: - host = ( - api_mtls_endpoint - if ":" in api_mtls_endpoint - else api_mtls_endpoint + ":443" - ) + self._ssl_channel_credentials = None + + else: + if api_mtls_endpoint: + host = api_mtls_endpoint + + # Create SSL credentials with client_cert_source or application + # default SSL credentials. + if client_cert_source: + cert, key = client_cert_source() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + else: + self._ssl_channel_credentials = SslCredentials().ssl_credentials - # Create SSL credentials with client_cert_source or application - # default SSL credentials. - if client_cert_source: - cert, key = client_cert_source() - ssl_credentials = grpc.ssl_channel_credentials( - certificate_chain=cert, private_key=key - ) else: - ssl_credentials = SslCredentials().ssl_credentials - - # create a new channel. The provided one is ignored. - self._grpc_channel = type(self).create_channel( - host, - credentials=credentials, - credentials_file=credentials_file, - ssl_credentials=ssl_credentials, - scopes=scopes or self.AUTH_SCOPES, - quota_project_id=quota_project_id, - ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) - # Run the base constructor. + # The base transport sets the host, credentials and scopes super().__init__( host=host, credentials=credentials, credentials_file=credentials_file, - scopes=scopes or self.AUTH_SCOPES, + scopes=scopes, quota_project_id=quota_project_id, client_info=client_info, ) - self._stubs = {} + if not self._grpc_channel: + self._grpc_channel = type(self).create_channel( + self._host, + credentials=self._credentials, + credentials_file=credentials_file, + scopes=self._scopes, + ssl_credentials=self._ssl_channel_credentials, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Wrap messages. This must be done after self._grpc_channel exists + self._prep_wrapped_messages(client_info) @property def grpc_channel(self) -> aio.Channel: @@ -201,13 +229,6 @@ def grpc_channel(self) -> aio.Channel: This property caches on the instance; repeated calls return the same channel. """ - # Sanity check: Only create a new channel if we do not already - # have one. - if not hasattr(self, "_grpc_channel"): - self._grpc_channel = self.create_channel( - self._host, credentials=self._credentials, - ) - # Return the channel from cache. return self._grpc_channel diff --git a/google/cloud/trace_v2/types/__init__.py b/google/cloud/trace_v2/types/__init__.py index 4b94729a..27530d1f 100644 --- a/google/cloud/trace_v2/types/__init__.py +++ b/google/cloud/trace_v2/types/__init__.py @@ -16,20 +16,19 @@ # from .trace import ( - Span, AttributeValue, - StackTrace, Module, + Span, + StackTrace, TruncatableString, ) from .tracing import BatchWriteSpansRequest - __all__ = ( - "Span", "AttributeValue", - "StackTrace", "Module", + "Span", + "StackTrace", "TruncatableString", "BatchWriteSpansRequest", ) diff --git a/google/cloud/trace_v2/types/trace.py b/google/cloud/trace_v2/types/trace.py index ab8773bb..5a3b1497 100644 --- a/google/cloud/trace_v2/types/trace.py +++ b/google/cloud/trace_v2/types/trace.py @@ -59,7 +59,7 @@ class Span(proto.Message): parent_span_id (str): The [SPAN_ID] of this span's parent span. If this is a root span, then this field must be empty. - display_name (~.trace.TruncatableString): + display_name (google.cloud.trace_v2.types.TruncatableString): Required. A description of the span's operation (up to 128 bytes). Stackdriver Trace displays the description in the Google Cloud @@ -71,44 +71,44 @@ class Span(proto.Message): application and at the same call point. This makes it easier to correlate spans in different traces. - start_time (~.timestamp.Timestamp): + start_time (google.protobuf.timestamp_pb2.Timestamp): Required. The start time of the span. On the client side, this is the time kept by the local machine where the span execution starts. On the server side, this is the time when the server's application handler starts running. - end_time (~.timestamp.Timestamp): + end_time (google.protobuf.timestamp_pb2.Timestamp): Required. The end time of the span. On the client side, this is the time kept by the local machine where the span execution ends. On the server side, this is the time when the server application handler stops running. - attributes (~.trace.Span.Attributes): + attributes (google.cloud.trace_v2.types.Span.Attributes): A set of attributes on the span. You can have up to 32 attributes per span. - stack_trace (~.trace.StackTrace): + stack_trace (google.cloud.trace_v2.types.StackTrace): Stack trace captured at the start of the span. - time_events (~.trace.Span.TimeEvents): + time_events (google.cloud.trace_v2.types.Span.TimeEvents): A set of time events. You can have up to 32 annotations and 128 message events per span. - links (~.trace.Span.Links): + links (google.cloud.trace_v2.types.Span.Links): Links associated with the span. You can have up to 128 links per Span. - status (~.gr_status.Status): + status (google.rpc.status_pb2.Status): Optional. The final status for this span. - same_process_as_parent_span (~.wrappers.BoolValue): + same_process_as_parent_span (google.protobuf.wrappers_pb2.BoolValue): Optional. Set this parameter to indicate whether this span is in the same process as its parent. If you do not set this parameter, Stackdriver Trace is unable to take advantage of this helpful information. - child_span_count (~.wrappers.Int32Value): + child_span_count (google.protobuf.wrappers_pb2.Int32Value): Optional. The number of child spans that were generated while this span was active. If set, allows implementation to detect missing child spans. - span_kind (~.trace.Span.SpanKind): + span_kind (google.cloud.trace_v2.types.Span.SpanKind): Optional. Distinguishes between spans generated in a particular context. For example, two spans with the same name may be distinguished using ``CLIENT`` (caller) and @@ -130,7 +130,7 @@ class Attributes(proto.Message): r"""A set of attributes, each in the format ``[KEY]:[VALUE]``. Attributes: - attribute_map (Sequence[~.trace.Span.Attributes.AttributeMapEntry]): + attribute_map (Sequence[google.cloud.trace_v2.types.Span.Attributes.AttributeMapEntry]): The set of attributes. Each attribute's key can be up to 128 bytes long. The value can be a string up to 256 bytes, a signed 64-bit integer, or the Boolean values ``true`` and @@ -159,12 +159,12 @@ class TimeEvent(proto.Message): r"""A time-stamped annotation or message event in the Span. Attributes: - time (~.timestamp.Timestamp): + time (google.protobuf.timestamp_pb2.Timestamp): The timestamp indicating the time the event occurred. - annotation (~.trace.Span.TimeEvent.Annotation): + annotation (google.cloud.trace_v2.types.Span.TimeEvent.Annotation): Text annotation with a set of attributes. - message_event (~.trace.Span.TimeEvent.MessageEvent): + message_event (google.cloud.trace_v2.types.Span.TimeEvent.MessageEvent): An event describing a message sent/received between Spans. """ @@ -173,11 +173,11 @@ class Annotation(proto.Message): r"""Text annotation with a set of attributes. Attributes: - description (~.trace.TruncatableString): + description (google.cloud.trace_v2.types.TruncatableString): A user-supplied message describing the event. The maximum length for the description is 256 bytes. - attributes (~.trace.Span.Attributes): + attributes (google.cloud.trace_v2.types.Span.Attributes): A set of attributes on the annotation. You can have up to 4 attributes per Annotation. """ @@ -194,7 +194,7 @@ class MessageEvent(proto.Message): r"""An event describing a message sent/received between Spans. Attributes: - type (~.trace.Span.TimeEvent.MessageEvent.Type): + type_ (google.cloud.trace_v2.types.Span.TimeEvent.MessageEvent.Type): Type of MessageEvent. Indicates whether the message was sent or received. id (int): @@ -217,7 +217,7 @@ class Type(proto.Enum): SENT = 1 RECEIVED = 2 - type = proto.Field( + type_ = proto.Field( proto.ENUM, number=1, enum="Span.TimeEvent.MessageEvent.Type", ) @@ -246,7 +246,7 @@ class TimeEvents(proto.Message): pairs, or details of a message sent/received between Spans. Attributes: - time_event (Sequence[~.trace.Span.TimeEvent]): + time_event (Sequence[google.cloud.trace_v2.types.Span.TimeEvent]): A collection of ``TimeEvent``\ s. dropped_annotations_count (int): The number of dropped annotations in all the @@ -278,10 +278,10 @@ class Link(proto.Message): The [TRACE_ID] for a trace within a project. span_id (str): The [SPAN_ID] for a span within a trace. - type (~.trace.Span.Link.Type): + type_ (google.cloud.trace_v2.types.Span.Link.Type): The relationship of the current span relative to the linked span. - attributes (~.trace.Span.Attributes): + attributes (google.cloud.trace_v2.types.Span.Attributes): A set of attributes on the link. You have have up to 32 attributes per link. """ @@ -298,7 +298,7 @@ class Type(proto.Enum): span_id = proto.Field(proto.STRING, number=2) - type = proto.Field(proto.ENUM, number=3, enum="Span.Link.Type",) + type_ = proto.Field(proto.ENUM, number=3, enum="Span.Link.Type",) attributes = proto.Field(proto.MESSAGE, number=4, message="Span.Attributes",) @@ -307,7 +307,7 @@ class Links(proto.Message): a span in the same or different trace. Attributes: - link (Sequence[~.trace.Span.Link]): + link (Sequence[google.cloud.trace_v2.types.Span.Link]): A collection of links. dropped_links_count (int): The number of dropped links after the maximum @@ -356,7 +356,7 @@ class AttributeValue(proto.Message): r"""The allowed types for [VALUE] in a ``[KEY]:[VALUE]`` attribute. Attributes: - string_value (~.trace.TruncatableString): + string_value (google.cloud.trace_v2.types.TruncatableString): A string up to 256 bytes long. int_value (int): A 64-bit signed integer. @@ -377,7 +377,7 @@ class StackTrace(proto.Message): r"""A call stack appearing in a trace. Attributes: - stack_frames (~.trace.StackTrace.StackFrames): + stack_frames (google.cloud.trace_v2.types.StackTrace.StackFrames): Stack frames in this stack trace. A maximum of 128 frames are allowed. stack_trace_hash_id (int): @@ -396,15 +396,15 @@ class StackFrame(proto.Message): r"""Represents a single stack frame in a stack trace. Attributes: - function_name (~.trace.TruncatableString): + function_name (google.cloud.trace_v2.types.TruncatableString): The fully-qualified name that uniquely identifies the function or method that is active in this frame (up to 1024 bytes). - original_function_name (~.trace.TruncatableString): + original_function_name (google.cloud.trace_v2.types.TruncatableString): An un-mangled function name, if ``function_name`` is `mangled `__. The name can be fully-qualified (up to 1024 bytes). - file_name (~.trace.TruncatableString): + file_name (google.cloud.trace_v2.types.TruncatableString): The name of the source file where the function call appears (up to 256 bytes). line_number (int): @@ -414,10 +414,10 @@ class StackFrame(proto.Message): The column number where the function call appears, if available. This is important in JavaScript because of its anonymous functions. - load_module (~.trace.Module): + load_module (google.cloud.trace_v2.types.Module): The binary module from where the code was loaded. - source_version (~.trace.TruncatableString): + source_version (google.cloud.trace_v2.types.TruncatableString): The version of the deployed source code (up to 128 bytes). """ @@ -446,7 +446,7 @@ class StackFrames(proto.Message): r"""A collection of stack frames, which can be truncated. Attributes: - frame (Sequence[~.trace.StackTrace.StackFrame]): + frame (Sequence[google.cloud.trace_v2.types.StackTrace.StackFrame]): Stack frames in this call stack. dropped_frames_count (int): The number of stack frames that were dropped @@ -470,11 +470,11 @@ class Module(proto.Message): r"""Binary module. Attributes: - module (~.trace.TruncatableString): + module (google.cloud.trace_v2.types.TruncatableString): For example: main binary, kernel modules, and dynamic libraries such as libc.so, sharedlib.so (up to 256 bytes). - build_id (~.trace.TruncatableString): + build_id (google.cloud.trace_v2.types.TruncatableString): A unique identifier for the module, usually a hash of its contents (up to 128 bytes). """ diff --git a/google/cloud/trace_v2/types/tracing.py b/google/cloud/trace_v2/types/tracing.py index b2eaff79..57269d35 100644 --- a/google/cloud/trace_v2/types/tracing.py +++ b/google/cloud/trace_v2/types/tracing.py @@ -33,7 +33,7 @@ class BatchWriteSpansRequest(proto.Message): name (str): Required. The name of the project where the spans belong. The format is ``projects/[PROJECT_ID]``. - spans (Sequence[~.trace.Span]): + spans (Sequence[google.cloud.trace_v2.types.Span]): Required. A list of new spans. The span names must not match existing spans, or the results are undefined. diff --git a/google/devtools/cloudtrace/__init__.py b/google/devtools/cloudtrace/__init__.py deleted file mode 100644 index 27a6340d..00000000 --- a/google/devtools/cloudtrace/__init__.py +++ /dev/null @@ -1,40 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright 2020 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. -# - -from google.devtools.cloudtrace_v2.services.trace_service.async_client import ( - TraceServiceAsyncClient, -) -from google.devtools.cloudtrace_v2.services.trace_service.client import ( - TraceServiceClient, -) -from google.devtools.cloudtrace_v2.types.trace import AttributeValue -from google.devtools.cloudtrace_v2.types.trace import Module -from google.devtools.cloudtrace_v2.types.trace import Span -from google.devtools.cloudtrace_v2.types.trace import StackTrace -from google.devtools.cloudtrace_v2.types.trace import TruncatableString -from google.devtools.cloudtrace_v2.types.tracing import BatchWriteSpansRequest - -__all__ = ( - "AttributeValue", - "BatchWriteSpansRequest", - "Module", - "Span", - "StackTrace", - "TraceServiceAsyncClient", - "TraceServiceClient", - "TruncatableString", -) diff --git a/google/devtools/cloudtrace/py.typed b/google/devtools/cloudtrace/py.typed deleted file mode 100644 index c9f021ea..00000000 --- a/google/devtools/cloudtrace/py.typed +++ /dev/null @@ -1,2 +0,0 @@ -# Marker file for PEP 561. -# The google-devtools-cloudtrace package uses inline types. diff --git a/google/devtools/cloudtrace_v1/py.typed b/google/devtools/cloudtrace_v1/py.typed deleted file mode 100644 index c9f021ea..00000000 --- a/google/devtools/cloudtrace_v1/py.typed +++ /dev/null @@ -1,2 +0,0 @@ -# Marker file for PEP 561. -# The google-devtools-cloudtrace package uses inline types. diff --git a/google/devtools/cloudtrace_v1/services/__init__.py b/google/devtools/cloudtrace_v1/services/__init__.py deleted file mode 100644 index 42ffdf2b..00000000 --- a/google/devtools/cloudtrace_v1/services/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright 2020 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. -# diff --git a/google/devtools/cloudtrace_v1/services/trace_service/__init__.py b/google/devtools/cloudtrace_v1/services/trace_service/__init__.py deleted file mode 100644 index e06e796c..00000000 --- a/google/devtools/cloudtrace_v1/services/trace_service/__init__.py +++ /dev/null @@ -1,24 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright 2020 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. -# - -from .client import TraceServiceClient -from .async_client import TraceServiceAsyncClient - -__all__ = ( - "TraceServiceClient", - "TraceServiceAsyncClient", -) diff --git a/google/devtools/cloudtrace_v1/services/trace_service/async_client.py b/google/devtools/cloudtrace_v1/services/trace_service/async_client.py deleted file mode 100644 index 29f96058..00000000 --- a/google/devtools/cloudtrace_v1/services/trace_service/async_client.py +++ /dev/null @@ -1,438 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright 2020 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. -# - -from collections import OrderedDict -import functools -import re -from typing import Dict, Sequence, Tuple, Type, Union -import pkg_resources - -import google.api_core.client_options as ClientOptions # type: ignore -from google.api_core import exceptions # type: ignore -from google.api_core import gapic_v1 # type: ignore -from google.api_core import retry as retries # type: ignore -from google.auth import credentials # type: ignore -from google.oauth2 import service_account # type: ignore - -from google.devtools.cloudtrace_v1.services.trace_service import pagers -from google.devtools.cloudtrace_v1.types import trace - -from .transports.base import TraceServiceTransport, DEFAULT_CLIENT_INFO -from .transports.grpc_asyncio import TraceServiceGrpcAsyncIOTransport -from .client import TraceServiceClient - - -class TraceServiceAsyncClient: - """This file describes an API for collecting and viewing traces - and spans within a trace. A Trace is a collection of spans - corresponding to a single operation or set of operations for an - application. A span is an individual timed event which forms a - node of the trace tree. Spans for a single trace may span - multiple services. - """ - - _client: TraceServiceClient - - DEFAULT_ENDPOINT = TraceServiceClient.DEFAULT_ENDPOINT - DEFAULT_MTLS_ENDPOINT = TraceServiceClient.DEFAULT_MTLS_ENDPOINT - - common_billing_account_path = staticmethod( - TraceServiceClient.common_billing_account_path - ) - parse_common_billing_account_path = staticmethod( - TraceServiceClient.parse_common_billing_account_path - ) - - common_folder_path = staticmethod(TraceServiceClient.common_folder_path) - parse_common_folder_path = staticmethod(TraceServiceClient.parse_common_folder_path) - - common_organization_path = staticmethod(TraceServiceClient.common_organization_path) - parse_common_organization_path = staticmethod( - TraceServiceClient.parse_common_organization_path - ) - - common_project_path = staticmethod(TraceServiceClient.common_project_path) - parse_common_project_path = staticmethod( - TraceServiceClient.parse_common_project_path - ) - - common_location_path = staticmethod(TraceServiceClient.common_location_path) - parse_common_location_path = staticmethod( - TraceServiceClient.parse_common_location_path - ) - - @classmethod - def from_service_account_info(cls, info: dict, *args, **kwargs): - """Creates an instance of this client using the provided credentials info. - - Args: - info (dict): The service account private key info. - args: Additional arguments to pass to the constructor. - kwargs: Additional arguments to pass to the constructor. - - Returns: - TraceServiceAsyncClient: The constructed client. - """ - return TraceServiceClient.from_service_account_info.__func__(TraceServiceAsyncClient, info, *args, **kwargs) # type: ignore - - @classmethod - def from_service_account_file(cls, filename: str, *args, **kwargs): - """Creates an instance of this client using the provided credentials - file. - - Args: - filename (str): The path to the service account private key json - file. - args: Additional arguments to pass to the constructor. - kwargs: Additional arguments to pass to the constructor. - - Returns: - TraceServiceAsyncClient: The constructed client. - """ - return TraceServiceClient.from_service_account_file.__func__(TraceServiceAsyncClient, filename, *args, **kwargs) # type: ignore - - from_service_account_json = from_service_account_file - - @property - def transport(self) -> TraceServiceTransport: - """Return the transport used by the client instance. - - Returns: - TraceServiceTransport: The transport used by the client instance. - """ - return self._client.transport - - get_transport_class = functools.partial( - type(TraceServiceClient).get_transport_class, type(TraceServiceClient) - ) - - def __init__( - self, - *, - credentials: credentials.Credentials = None, - transport: Union[str, TraceServiceTransport] = "grpc_asyncio", - client_options: ClientOptions = None, - client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, - ) -> None: - """Instantiate the trace service client. - - Args: - credentials (Optional[google.auth.credentials.Credentials]): The - authorization credentials to attach to requests. These - credentials identify the application to the service; if none - are specified, the client will attempt to ascertain the - credentials from the environment. - transport (Union[str, ~.TraceServiceTransport]): The - transport to use. If set to None, a transport is chosen - automatically. - client_options (ClientOptions): Custom options for the client. It - won't take effect if a ``transport`` instance is provided. - (1) The ``api_endpoint`` property can be used to override the - default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT - environment variable can also be used to override the endpoint: - "always" (always use the default mTLS endpoint), "never" (always - use the default regular endpoint) and "auto" (auto switch to the - default mTLS endpoint if client certificate is present, this is - the default value). However, the ``api_endpoint`` property takes - precedence if provided. - (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable - is "true", then the ``client_cert_source`` property can be used - to provide client certificate for mutual TLS transport. If - not provided, the default SSL client certificate will be used if - present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not - set, no client certificate will be used. - - Raises: - google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport - creation failed for any reason. - """ - - self._client = TraceServiceClient( - credentials=credentials, - transport=transport, - client_options=client_options, - client_info=client_info, - ) - - async def list_traces( - self, - request: trace.ListTracesRequest = None, - *, - project_id: str = None, - retry: retries.Retry = gapic_v1.method.DEFAULT, - timeout: float = None, - metadata: Sequence[Tuple[str, str]] = (), - ) -> pagers.ListTracesAsyncPager: - r"""Returns of a list of traces that match the specified - filter conditions. - - Args: - request (:class:`google.devtools.cloudtrace_v1.types.ListTracesRequest`): - The request object. The request message for the - `ListTraces` method. All fields are required unless - specified. - project_id (:class:`str`): - Required. ID of the Cloud project - where the trace data is stored. - - This corresponds to the ``project_id`` field - on the ``request`` instance; if ``request`` is provided, this - should not be set. - - retry (google.api_core.retry.Retry): Designation of what errors, if any, - should be retried. - timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. - - Returns: - google.devtools.cloudtrace_v1.services.trace_service.pagers.ListTracesAsyncPager: - The response message for the ListTraces method. - - Iterating over this object will yield results and - resolve additional pages automatically. - - """ - # Create or coerce a protobuf request object. - # Sanity check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. - has_flattened_params = any([project_id]) - if request is not None and has_flattened_params: - raise ValueError( - "If the `request` argument is set, then none of " - "the individual field arguments should be set." - ) - - request = trace.ListTracesRequest(request) - - # If we have keyword arguments corresponding to fields on the - # request, apply these. - - if project_id is not None: - request.project_id = project_id - - # Wrap the RPC method; this adds retry and timeout information, - # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.list_traces, - default_retry=retries.Retry( - initial=0.1, - maximum=1.0, - multiplier=1.2, - predicate=retries.if_exception_type( - exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, - ), - deadline=45.0, - ), - default_timeout=45.0, - client_info=DEFAULT_CLIENT_INFO, - ) - - # Send the request. - response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) - - # This method is paged; wrap the response in a pager, which provides - # an `__aiter__` convenience method. - response = pagers.ListTracesAsyncPager( - method=rpc, request=request, response=response, metadata=metadata, - ) - - # Done; return the response. - return response - - async def get_trace( - self, - request: trace.GetTraceRequest = None, - *, - project_id: str = None, - trace_id: str = None, - retry: retries.Retry = gapic_v1.method.DEFAULT, - timeout: float = None, - metadata: Sequence[Tuple[str, str]] = (), - ) -> trace.Trace: - r"""Gets a single trace by its ID. - - Args: - request (:class:`google.devtools.cloudtrace_v1.types.GetTraceRequest`): - The request object. The request message for the - `GetTrace` method. - project_id (:class:`str`): - Required. ID of the Cloud project - where the trace data is stored. - - This corresponds to the ``project_id`` field - on the ``request`` instance; if ``request`` is provided, this - should not be set. - trace_id (:class:`str`): - Required. ID of the trace to return. - This corresponds to the ``trace_id`` field - on the ``request`` instance; if ``request`` is provided, this - should not be set. - - retry (google.api_core.retry.Retry): Designation of what errors, if any, - should be retried. - timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. - - Returns: - google.devtools.cloudtrace_v1.types.Trace: - A trace describes how long it takes - for an application to perform an - operation. It consists of a set of - spans, each of which represent a single - timed event within the operation. - - """ - # Create or coerce a protobuf request object. - # Sanity check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. - has_flattened_params = any([project_id, trace_id]) - if request is not None and has_flattened_params: - raise ValueError( - "If the `request` argument is set, then none of " - "the individual field arguments should be set." - ) - - request = trace.GetTraceRequest(request) - - # If we have keyword arguments corresponding to fields on the - # request, apply these. - - if project_id is not None: - request.project_id = project_id - if trace_id is not None: - request.trace_id = trace_id - - # Wrap the RPC method; this adds retry and timeout information, - # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.get_trace, - default_retry=retries.Retry( - initial=0.1, - maximum=1.0, - multiplier=1.2, - predicate=retries.if_exception_type( - exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, - ), - deadline=45.0, - ), - default_timeout=45.0, - client_info=DEFAULT_CLIENT_INFO, - ) - - # Send the request. - response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) - - # Done; return the response. - return response - - async def patch_traces( - self, - request: trace.PatchTracesRequest = None, - *, - project_id: str = None, - traces: trace.Traces = None, - retry: retries.Retry = gapic_v1.method.DEFAULT, - timeout: float = None, - metadata: Sequence[Tuple[str, str]] = (), - ) -> None: - r"""Sends new traces to Stackdriver Trace or updates - existing traces. If the ID of a trace that you send - matches that of an existing trace, any fields in the - existing trace and its spans are overwritten by the - provided values, and any new fields provided are merged - with the existing trace data. If the ID does not match, - a new trace is created. - - Args: - request (:class:`google.devtools.cloudtrace_v1.types.PatchTracesRequest`): - The request object. The request message for the - `PatchTraces` method. - project_id (:class:`str`): - Required. ID of the Cloud project - where the trace data is stored. - - This corresponds to the ``project_id`` field - on the ``request`` instance; if ``request`` is provided, this - should not be set. - traces (:class:`google.devtools.cloudtrace_v1.types.Traces`): - Required. The body of the message. - This corresponds to the ``traces`` field - on the ``request`` instance; if ``request`` is provided, this - should not be set. - - retry (google.api_core.retry.Retry): Designation of what errors, if any, - should be retried. - timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. - """ - # Create or coerce a protobuf request object. - # Sanity check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. - has_flattened_params = any([project_id, traces]) - if request is not None and has_flattened_params: - raise ValueError( - "If the `request` argument is set, then none of " - "the individual field arguments should be set." - ) - - request = trace.PatchTracesRequest(request) - - # If we have keyword arguments corresponding to fields on the - # request, apply these. - - if project_id is not None: - request.project_id = project_id - if traces is not None: - request.traces = traces - - # Wrap the RPC method; this adds retry and timeout information, - # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.patch_traces, - default_retry=retries.Retry( - initial=0.1, - maximum=1.0, - multiplier=1.2, - predicate=retries.if_exception_type( - exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, - ), - deadline=45.0, - ), - default_timeout=45.0, - client_info=DEFAULT_CLIENT_INFO, - ) - - # Send the request. - await rpc( - request, retry=retry, timeout=timeout, metadata=metadata, - ) - - -try: - DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( - gapic_version=pkg_resources.get_distribution( - "google-devtools-cloudtrace", - ).version, - ) -except pkg_resources.DistributionNotFound: - DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() - - -__all__ = ("TraceServiceAsyncClient",) diff --git a/google/devtools/cloudtrace_v1/services/trace_service/client.py b/google/devtools/cloudtrace_v1/services/trace_service/client.py deleted file mode 100644 index 6c5aeee3..00000000 --- a/google/devtools/cloudtrace_v1/services/trace_service/client.py +++ /dev/null @@ -1,577 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright 2020 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. -# - -from collections import OrderedDict -from distutils import util -import os -import re -from typing import Callable, Dict, Optional, Sequence, Tuple, Type, Union -import pkg_resources - -from google.api_core import client_options as client_options_lib # type: ignore -from google.api_core import exceptions # type: ignore -from google.api_core import gapic_v1 # type: ignore -from google.api_core import retry as retries # type: ignore -from google.auth import credentials # type: ignore -from google.auth.transport import mtls # type: ignore -from google.auth.transport.grpc import SslCredentials # type: ignore -from google.auth.exceptions import MutualTLSChannelError # type: ignore -from google.oauth2 import service_account # type: ignore - -from google.devtools.cloudtrace_v1.services.trace_service import pagers -from google.devtools.cloudtrace_v1.types import trace - -from .transports.base import TraceServiceTransport, DEFAULT_CLIENT_INFO -from .transports.grpc import TraceServiceGrpcTransport -from .transports.grpc_asyncio import TraceServiceGrpcAsyncIOTransport - - -class TraceServiceClientMeta(type): - """Metaclass for the TraceService client. - - This provides class-level methods for building and retrieving - support objects (e.g. transport) without polluting the client instance - objects. - """ - - _transport_registry = OrderedDict() # type: Dict[str, Type[TraceServiceTransport]] - _transport_registry["grpc"] = TraceServiceGrpcTransport - _transport_registry["grpc_asyncio"] = TraceServiceGrpcAsyncIOTransport - - def get_transport_class(cls, label: str = None,) -> Type[TraceServiceTransport]: - """Return an appropriate transport class. - - Args: - label: The name of the desired transport. If none is - provided, then the first transport in the registry is used. - - Returns: - The transport class to use. - """ - # If a specific transport is requested, return that one. - if label: - return cls._transport_registry[label] - - # No transport is requested; return the default (that is, the first one - # in the dictionary). - return next(iter(cls._transport_registry.values())) - - -class TraceServiceClient(metaclass=TraceServiceClientMeta): - """This file describes an API for collecting and viewing traces - and spans within a trace. A Trace is a collection of spans - corresponding to a single operation or set of operations for an - application. A span is an individual timed event which forms a - node of the trace tree. Spans for a single trace may span - multiple services. - """ - - @staticmethod - def _get_default_mtls_endpoint(api_endpoint): - """Convert api endpoint to mTLS endpoint. - Convert "*.sandbox.googleapis.com" and "*.googleapis.com" to - "*.mtls.sandbox.googleapis.com" and "*.mtls.googleapis.com" respectively. - Args: - api_endpoint (Optional[str]): the api endpoint to convert. - Returns: - str: converted mTLS api endpoint. - """ - if not api_endpoint: - return api_endpoint - - mtls_endpoint_re = re.compile( - r"(?P[^.]+)(?P\.mtls)?(?P\.sandbox)?(?P\.googleapis\.com)?" - ) - - m = mtls_endpoint_re.match(api_endpoint) - name, mtls, sandbox, googledomain = m.groups() - if mtls or not googledomain: - return api_endpoint - - if sandbox: - return api_endpoint.replace( - "sandbox.googleapis.com", "mtls.sandbox.googleapis.com" - ) - - return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com") - - DEFAULT_ENDPOINT = "cloudtrace.googleapis.com" - DEFAULT_MTLS_ENDPOINT = _get_default_mtls_endpoint.__func__( # type: ignore - DEFAULT_ENDPOINT - ) - - @classmethod - def from_service_account_info(cls, info: dict, *args, **kwargs): - """Creates an instance of this client using the provided credentials info. - - Args: - info (dict): The service account private key info. - args: Additional arguments to pass to the constructor. - kwargs: Additional arguments to pass to the constructor. - - Returns: - TraceServiceClient: The constructed client. - """ - credentials = service_account.Credentials.from_service_account_info(info) - kwargs["credentials"] = credentials - return cls(*args, **kwargs) - - @classmethod - def from_service_account_file(cls, filename: str, *args, **kwargs): - """Creates an instance of this client using the provided credentials - file. - - Args: - filename (str): The path to the service account private key json - file. - args: Additional arguments to pass to the constructor. - kwargs: Additional arguments to pass to the constructor. - - Returns: - TraceServiceClient: The constructed client. - """ - credentials = service_account.Credentials.from_service_account_file(filename) - kwargs["credentials"] = credentials - return cls(*args, **kwargs) - - from_service_account_json = from_service_account_file - - @property - def transport(self) -> TraceServiceTransport: - """Return the transport used by the client instance. - - Returns: - TraceServiceTransport: The transport used by the client instance. - """ - return self._transport - - @staticmethod - def common_billing_account_path(billing_account: str,) -> str: - """Return a fully-qualified billing_account string.""" - return "billingAccounts/{billing_account}".format( - billing_account=billing_account, - ) - - @staticmethod - def parse_common_billing_account_path(path: str) -> Dict[str, str]: - """Parse a billing_account path into its component segments.""" - m = re.match(r"^billingAccounts/(?P.+?)$", path) - return m.groupdict() if m else {} - - @staticmethod - def common_folder_path(folder: str,) -> str: - """Return a fully-qualified folder string.""" - return "folders/{folder}".format(folder=folder,) - - @staticmethod - def parse_common_folder_path(path: str) -> Dict[str, str]: - """Parse a folder path into its component segments.""" - m = re.match(r"^folders/(?P.+?)$", path) - return m.groupdict() if m else {} - - @staticmethod - def common_organization_path(organization: str,) -> str: - """Return a fully-qualified organization string.""" - return "organizations/{organization}".format(organization=organization,) - - @staticmethod - def parse_common_organization_path(path: str) -> Dict[str, str]: - """Parse a organization path into its component segments.""" - m = re.match(r"^organizations/(?P.+?)$", path) - return m.groupdict() if m else {} - - @staticmethod - def common_project_path(project: str,) -> str: - """Return a fully-qualified project string.""" - return "projects/{project}".format(project=project,) - - @staticmethod - def parse_common_project_path(path: str) -> Dict[str, str]: - """Parse a project path into its component segments.""" - m = re.match(r"^projects/(?P.+?)$", path) - return m.groupdict() if m else {} - - @staticmethod - def common_location_path(project: str, location: str,) -> str: - """Return a fully-qualified location string.""" - return "projects/{project}/locations/{location}".format( - project=project, location=location, - ) - - @staticmethod - def parse_common_location_path(path: str) -> Dict[str, str]: - """Parse a location path into its component segments.""" - m = re.match(r"^projects/(?P.+?)/locations/(?P.+?)$", path) - return m.groupdict() if m else {} - - def __init__( - self, - *, - credentials: Optional[credentials.Credentials] = None, - transport: Union[str, TraceServiceTransport, None] = None, - client_options: Optional[client_options_lib.ClientOptions] = None, - client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, - ) -> None: - """Instantiate the trace service client. - - Args: - credentials (Optional[google.auth.credentials.Credentials]): The - authorization credentials to attach to requests. These - credentials identify the application to the service; if none - are specified, the client will attempt to ascertain the - credentials from the environment. - transport (Union[str, TraceServiceTransport]): The - transport to use. If set to None, a transport is chosen - automatically. - client_options (google.api_core.client_options.ClientOptions): Custom options for the - client. It won't take effect if a ``transport`` instance is provided. - (1) The ``api_endpoint`` property can be used to override the - default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT - environment variable can also be used to override the endpoint: - "always" (always use the default mTLS endpoint), "never" (always - use the default regular endpoint) and "auto" (auto switch to the - default mTLS endpoint if client certificate is present, this is - the default value). However, the ``api_endpoint`` property takes - precedence if provided. - (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable - is "true", then the ``client_cert_source`` property can be used - to provide client certificate for mutual TLS transport. If - not provided, the default SSL client certificate will be used if - present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not - set, no client certificate will be used. - client_info (google.api_core.gapic_v1.client_info.ClientInfo): - The client info used to send a user-agent string along with - API requests. If ``None``, then default info will be used. - Generally, you only need to set this if you're developing - your own client library. - - Raises: - google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport - creation failed for any reason. - """ - if isinstance(client_options, dict): - client_options = client_options_lib.from_dict(client_options) - if client_options is None: - client_options = client_options_lib.ClientOptions() - - # Create SSL credentials for mutual TLS if needed. - use_client_cert = bool( - util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) - ) - - client_cert_source_func = None - is_mtls = False - if use_client_cert: - if client_options.client_cert_source: - is_mtls = True - client_cert_source_func = client_options.client_cert_source - else: - is_mtls = mtls.has_default_client_cert_source() - client_cert_source_func = ( - mtls.default_client_cert_source() if is_mtls else None - ) - - # Figure out which api endpoint to use. - if client_options.api_endpoint is not None: - api_endpoint = client_options.api_endpoint - else: - use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto") - if use_mtls_env == "never": - api_endpoint = self.DEFAULT_ENDPOINT - elif use_mtls_env == "always": - api_endpoint = self.DEFAULT_MTLS_ENDPOINT - elif use_mtls_env == "auto": - api_endpoint = ( - self.DEFAULT_MTLS_ENDPOINT if is_mtls else self.DEFAULT_ENDPOINT - ) - else: - raise MutualTLSChannelError( - "Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted values: never, auto, always" - ) - - # Save or instantiate the transport. - # Ordinarily, we provide the transport, but allowing a custom transport - # instance provides an extensibility point for unusual situations. - if isinstance(transport, TraceServiceTransport): - # transport is a TraceServiceTransport instance. - if credentials or client_options.credentials_file: - raise ValueError( - "When providing a transport instance, " - "provide its credentials directly." - ) - if client_options.scopes: - raise ValueError( - "When providing a transport instance, " - "provide its scopes directly." - ) - self._transport = transport - else: - Transport = type(self).get_transport_class(transport) - self._transport = Transport( - credentials=credentials, - credentials_file=client_options.credentials_file, - host=api_endpoint, - scopes=client_options.scopes, - client_cert_source_for_mtls=client_cert_source_func, - quota_project_id=client_options.quota_project_id, - client_info=client_info, - ) - - def list_traces( - self, - request: trace.ListTracesRequest = None, - *, - project_id: str = None, - retry: retries.Retry = gapic_v1.method.DEFAULT, - timeout: float = None, - metadata: Sequence[Tuple[str, str]] = (), - ) -> pagers.ListTracesPager: - r"""Returns of a list of traces that match the specified - filter conditions. - - Args: - request (google.devtools.cloudtrace_v1.types.ListTracesRequest): - The request object. The request message for the - `ListTraces` method. All fields are required unless - specified. - project_id (str): - Required. ID of the Cloud project - where the trace data is stored. - - This corresponds to the ``project_id`` field - on the ``request`` instance; if ``request`` is provided, this - should not be set. - - retry (google.api_core.retry.Retry): Designation of what errors, if any, - should be retried. - timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. - - Returns: - google.devtools.cloudtrace_v1.services.trace_service.pagers.ListTracesPager: - The response message for the ListTraces method. - - Iterating over this object will yield results and - resolve additional pages automatically. - - """ - # Create or coerce a protobuf request object. - # Sanity check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. - has_flattened_params = any([project_id]) - if request is not None and has_flattened_params: - raise ValueError( - "If the `request` argument is set, then none of " - "the individual field arguments should be set." - ) - - # Minor optimization to avoid making a copy if the user passes - # in a trace.ListTracesRequest. - # There's no risk of modifying the input as we've already verified - # there are no flattened fields. - if not isinstance(request, trace.ListTracesRequest): - request = trace.ListTracesRequest(request) - - # If we have keyword arguments corresponding to fields on the - # request, apply these. - - if project_id is not None: - request.project_id = project_id - - # Wrap the RPC method; this adds retry and timeout information, - # and friendly error handling. - rpc = self._transport._wrapped_methods[self._transport.list_traces] - - # Send the request. - response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) - - # This method is paged; wrap the response in a pager, which provides - # an `__iter__` convenience method. - response = pagers.ListTracesPager( - method=rpc, request=request, response=response, metadata=metadata, - ) - - # Done; return the response. - return response - - def get_trace( - self, - request: trace.GetTraceRequest = None, - *, - project_id: str = None, - trace_id: str = None, - retry: retries.Retry = gapic_v1.method.DEFAULT, - timeout: float = None, - metadata: Sequence[Tuple[str, str]] = (), - ) -> trace.Trace: - r"""Gets a single trace by its ID. - - Args: - request (google.devtools.cloudtrace_v1.types.GetTraceRequest): - The request object. The request message for the - `GetTrace` method. - project_id (str): - Required. ID of the Cloud project - where the trace data is stored. - - This corresponds to the ``project_id`` field - on the ``request`` instance; if ``request`` is provided, this - should not be set. - trace_id (str): - Required. ID of the trace to return. - This corresponds to the ``trace_id`` field - on the ``request`` instance; if ``request`` is provided, this - should not be set. - - retry (google.api_core.retry.Retry): Designation of what errors, if any, - should be retried. - timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. - - Returns: - google.devtools.cloudtrace_v1.types.Trace: - A trace describes how long it takes - for an application to perform an - operation. It consists of a set of - spans, each of which represent a single - timed event within the operation. - - """ - # Create or coerce a protobuf request object. - # Sanity check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. - has_flattened_params = any([project_id, trace_id]) - if request is not None and has_flattened_params: - raise ValueError( - "If the `request` argument is set, then none of " - "the individual field arguments should be set." - ) - - # Minor optimization to avoid making a copy if the user passes - # in a trace.GetTraceRequest. - # There's no risk of modifying the input as we've already verified - # there are no flattened fields. - if not isinstance(request, trace.GetTraceRequest): - request = trace.GetTraceRequest(request) - - # If we have keyword arguments corresponding to fields on the - # request, apply these. - - if project_id is not None: - request.project_id = project_id - if trace_id is not None: - request.trace_id = trace_id - - # Wrap the RPC method; this adds retry and timeout information, - # and friendly error handling. - rpc = self._transport._wrapped_methods[self._transport.get_trace] - - # Send the request. - response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) - - # Done; return the response. - return response - - def patch_traces( - self, - request: trace.PatchTracesRequest = None, - *, - project_id: str = None, - traces: trace.Traces = None, - retry: retries.Retry = gapic_v1.method.DEFAULT, - timeout: float = None, - metadata: Sequence[Tuple[str, str]] = (), - ) -> None: - r"""Sends new traces to Stackdriver Trace or updates - existing traces. If the ID of a trace that you send - matches that of an existing trace, any fields in the - existing trace and its spans are overwritten by the - provided values, and any new fields provided are merged - with the existing trace data. If the ID does not match, - a new trace is created. - - Args: - request (google.devtools.cloudtrace_v1.types.PatchTracesRequest): - The request object. The request message for the - `PatchTraces` method. - project_id (str): - Required. ID of the Cloud project - where the trace data is stored. - - This corresponds to the ``project_id`` field - on the ``request`` instance; if ``request`` is provided, this - should not be set. - traces (google.devtools.cloudtrace_v1.types.Traces): - Required. The body of the message. - This corresponds to the ``traces`` field - on the ``request`` instance; if ``request`` is provided, this - should not be set. - - retry (google.api_core.retry.Retry): Designation of what errors, if any, - should be retried. - timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. - """ - # Create or coerce a protobuf request object. - # Sanity check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. - has_flattened_params = any([project_id, traces]) - if request is not None and has_flattened_params: - raise ValueError( - "If the `request` argument is set, then none of " - "the individual field arguments should be set." - ) - - # Minor optimization to avoid making a copy if the user passes - # in a trace.PatchTracesRequest. - # There's no risk of modifying the input as we've already verified - # there are no flattened fields. - if not isinstance(request, trace.PatchTracesRequest): - request = trace.PatchTracesRequest(request) - - # If we have keyword arguments corresponding to fields on the - # request, apply these. - - if project_id is not None: - request.project_id = project_id - if traces is not None: - request.traces = traces - - # Wrap the RPC method; this adds retry and timeout information, - # and friendly error handling. - rpc = self._transport._wrapped_methods[self._transport.patch_traces] - - # Send the request. - rpc( - request, retry=retry, timeout=timeout, metadata=metadata, - ) - - -try: - DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( - gapic_version=pkg_resources.get_distribution( - "google-devtools-cloudtrace", - ).version, - ) -except pkg_resources.DistributionNotFound: - DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() - - -__all__ = ("TraceServiceClient",) diff --git a/google/devtools/cloudtrace_v1/services/trace_service/pagers.py b/google/devtools/cloudtrace_v1/services/trace_service/pagers.py deleted file mode 100644 index 568691da..00000000 --- a/google/devtools/cloudtrace_v1/services/trace_service/pagers.py +++ /dev/null @@ -1,157 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright 2020 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. -# - -from typing import ( - Any, - AsyncIterable, - Awaitable, - Callable, - Iterable, - Sequence, - Tuple, - Optional, -) - -from google.devtools.cloudtrace_v1.types import trace - - -class ListTracesPager: - """A pager for iterating through ``list_traces`` requests. - - This class thinly wraps an initial - :class:`google.devtools.cloudtrace_v1.types.ListTracesResponse` object, and - provides an ``__iter__`` method to iterate through its - ``traces`` field. - - If there are more pages, the ``__iter__`` method will make additional - ``ListTraces`` requests and continue to iterate - through the ``traces`` field on the - corresponding responses. - - All the usual :class:`google.devtools.cloudtrace_v1.types.ListTracesResponse` - attributes are available on the pager. If multiple requests are made, only - the most recent response is retained, and thus used for attribute lookup. - """ - - def __init__( - self, - method: Callable[..., trace.ListTracesResponse], - request: trace.ListTracesRequest, - response: trace.ListTracesResponse, - *, - metadata: Sequence[Tuple[str, str]] = () - ): - """Instantiate the pager. - - Args: - method (Callable): The method that was originally called, and - which instantiated this pager. - request (google.devtools.cloudtrace_v1.types.ListTracesRequest): - The initial request object. - response (google.devtools.cloudtrace_v1.types.ListTracesResponse): - The initial response object. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. - """ - self._method = method - self._request = trace.ListTracesRequest(request) - self._response = response - self._metadata = metadata - - def __getattr__(self, name: str) -> Any: - return getattr(self._response, name) - - @property - def pages(self) -> Iterable[trace.ListTracesResponse]: - yield self._response - while self._response.next_page_token: - self._request.page_token = self._response.next_page_token - self._response = self._method(self._request, metadata=self._metadata) - yield self._response - - def __iter__(self) -> Iterable[trace.Trace]: - for page in self.pages: - yield from page.traces - - def __repr__(self) -> str: - return "{0}<{1!r}>".format(self.__class__.__name__, self._response) - - -class ListTracesAsyncPager: - """A pager for iterating through ``list_traces`` requests. - - This class thinly wraps an initial - :class:`google.devtools.cloudtrace_v1.types.ListTracesResponse` object, and - provides an ``__aiter__`` method to iterate through its - ``traces`` field. - - If there are more pages, the ``__aiter__`` method will make additional - ``ListTraces`` requests and continue to iterate - through the ``traces`` field on the - corresponding responses. - - All the usual :class:`google.devtools.cloudtrace_v1.types.ListTracesResponse` - attributes are available on the pager. If multiple requests are made, only - the most recent response is retained, and thus used for attribute lookup. - """ - - def __init__( - self, - method: Callable[..., Awaitable[trace.ListTracesResponse]], - request: trace.ListTracesRequest, - response: trace.ListTracesResponse, - *, - metadata: Sequence[Tuple[str, str]] = () - ): - """Instantiate the pager. - - Args: - method (Callable): The method that was originally called, and - which instantiated this pager. - request (google.devtools.cloudtrace_v1.types.ListTracesRequest): - The initial request object. - response (google.devtools.cloudtrace_v1.types.ListTracesResponse): - The initial response object. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. - """ - self._method = method - self._request = trace.ListTracesRequest(request) - self._response = response - self._metadata = metadata - - def __getattr__(self, name: str) -> Any: - return getattr(self._response, name) - - @property - async def pages(self) -> AsyncIterable[trace.ListTracesResponse]: - yield self._response - while self._response.next_page_token: - self._request.page_token = self._response.next_page_token - self._response = await self._method(self._request, metadata=self._metadata) - yield self._response - - def __aiter__(self) -> AsyncIterable[trace.Trace]: - async def async_generator(): - async for page in self.pages: - for response in page.traces: - yield response - - return async_generator() - - def __repr__(self) -> str: - return "{0}<{1!r}>".format(self.__class__.__name__, self._response) diff --git a/google/devtools/cloudtrace_v1/services/trace_service/transports/__init__.py b/google/devtools/cloudtrace_v1/services/trace_service/transports/__init__.py deleted file mode 100644 index b860866b..00000000 --- a/google/devtools/cloudtrace_v1/services/trace_service/transports/__init__.py +++ /dev/null @@ -1,35 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright 2020 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. -# - -from collections import OrderedDict -from typing import Dict, Type - -from .base import TraceServiceTransport -from .grpc import TraceServiceGrpcTransport -from .grpc_asyncio import TraceServiceGrpcAsyncIOTransport - - -# Compile a registry of transports. -_transport_registry = OrderedDict() # type: Dict[str, Type[TraceServiceTransport]] -_transport_registry["grpc"] = TraceServiceGrpcTransport -_transport_registry["grpc_asyncio"] = TraceServiceGrpcAsyncIOTransport - -__all__ = ( - "TraceServiceTransport", - "TraceServiceGrpcTransport", - "TraceServiceGrpcAsyncIOTransport", -) diff --git a/google/devtools/cloudtrace_v1/services/trace_service/transports/base.py b/google/devtools/cloudtrace_v1/services/trace_service/transports/base.py deleted file mode 100644 index efc90795..00000000 --- a/google/devtools/cloudtrace_v1/services/trace_service/transports/base.py +++ /dev/null @@ -1,188 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright 2020 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. -# - -import abc -import typing -import pkg_resources - -from google import auth # type: ignore -from google.api_core import exceptions # type: ignore -from google.api_core import gapic_v1 # type: ignore -from google.api_core import retry as retries # type: ignore -from google.auth import credentials # type: ignore - -from google.devtools.cloudtrace_v1.types import trace -from google.protobuf import empty_pb2 as empty # type: ignore - - -try: - DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( - gapic_version=pkg_resources.get_distribution( - "google-devtools-cloudtrace", - ).version, - ) -except pkg_resources.DistributionNotFound: - DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() - - -class TraceServiceTransport(abc.ABC): - """Abstract transport class for TraceService.""" - - AUTH_SCOPES = ( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/trace.append", - "https://www.googleapis.com/auth/trace.readonly", - ) - - def __init__( - self, - *, - host: str = "cloudtrace.googleapis.com", - credentials: credentials.Credentials = None, - credentials_file: typing.Optional[str] = None, - scopes: typing.Optional[typing.Sequence[str]] = AUTH_SCOPES, - quota_project_id: typing.Optional[str] = None, - client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, - **kwargs, - ) -> None: - """Instantiate the transport. - - Args: - host (Optional[str]): The hostname to connect to. - credentials (Optional[google.auth.credentials.Credentials]): The - authorization credentials to attach to requests. These - credentials identify the application to the service; if none - are specified, the client will attempt to ascertain the - credentials from the environment. - credentials_file (Optional[str]): A file with credentials that can - be loaded with :func:`google.auth.load_credentials_from_file`. - This argument is mutually exclusive with credentials. - scope (Optional[Sequence[str]]): A list of scopes. - quota_project_id (Optional[str]): An optional project to use for billing - and quota. - client_info (google.api_core.gapic_v1.client_info.ClientInfo): - The client info used to send a user-agent string along with - API requests. If ``None``, then default info will be used. - Generally, you only need to set this if you're developing - your own client library. - """ - # Save the hostname. Default to port 443 (HTTPS) if none is specified. - if ":" not in host: - host += ":443" - self._host = host - - # Save the scopes. - self._scopes = scopes or self.AUTH_SCOPES - - # If no credentials are provided, then determine the appropriate - # defaults. - if credentials and credentials_file: - raise exceptions.DuplicateCredentialArgs( - "'credentials_file' and 'credentials' are mutually exclusive" - ) - - if credentials_file is not None: - credentials, _ = auth.load_credentials_from_file( - credentials_file, scopes=self._scopes, quota_project_id=quota_project_id - ) - - elif credentials is None: - credentials, _ = auth.default( - scopes=self._scopes, quota_project_id=quota_project_id - ) - - # Save the credentials. - self._credentials = credentials - - def _prep_wrapped_messages(self, client_info): - # Precompute the wrapped methods. - self._wrapped_methods = { - self.list_traces: gapic_v1.method.wrap_method( - self.list_traces, - default_retry=retries.Retry( - initial=0.1, - maximum=1.0, - multiplier=1.2, - predicate=retries.if_exception_type( - exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, - ), - deadline=45.0, - ), - default_timeout=45.0, - client_info=client_info, - ), - self.get_trace: gapic_v1.method.wrap_method( - self.get_trace, - default_retry=retries.Retry( - initial=0.1, - maximum=1.0, - multiplier=1.2, - predicate=retries.if_exception_type( - exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, - ), - deadline=45.0, - ), - default_timeout=45.0, - client_info=client_info, - ), - self.patch_traces: gapic_v1.method.wrap_method( - self.patch_traces, - default_retry=retries.Retry( - initial=0.1, - maximum=1.0, - multiplier=1.2, - predicate=retries.if_exception_type( - exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, - ), - deadline=45.0, - ), - default_timeout=45.0, - client_info=client_info, - ), - } - - @property - def list_traces( - self, - ) -> typing.Callable[ - [trace.ListTracesRequest], - typing.Union[ - trace.ListTracesResponse, typing.Awaitable[trace.ListTracesResponse] - ], - ]: - raise NotImplementedError() - - @property - def get_trace( - self, - ) -> typing.Callable[ - [trace.GetTraceRequest], - typing.Union[trace.Trace, typing.Awaitable[trace.Trace]], - ]: - raise NotImplementedError() - - @property - def patch_traces( - self, - ) -> typing.Callable[ - [trace.PatchTracesRequest], - typing.Union[empty.Empty, typing.Awaitable[empty.Empty]], - ]: - raise NotImplementedError() - - -__all__ = ("TraceServiceTransport",) diff --git a/google/devtools/cloudtrace_v1/services/trace_service/transports/grpc.py b/google/devtools/cloudtrace_v1/services/trace_service/transports/grpc.py deleted file mode 100644 index 458dd49c..00000000 --- a/google/devtools/cloudtrace_v1/services/trace_service/transports/grpc.py +++ /dev/null @@ -1,312 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright 2020 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. -# - -import warnings -from typing import Callable, Dict, Optional, Sequence, Tuple - -from google.api_core import grpc_helpers # type: ignore -from google.api_core import gapic_v1 # type: ignore -from google import auth # type: ignore -from google.auth import credentials # type: ignore -from google.auth.transport.grpc import SslCredentials # type: ignore - -import grpc # type: ignore - -from google.devtools.cloudtrace_v1.types import trace -from google.protobuf import empty_pb2 as empty # type: ignore - -from .base import TraceServiceTransport, DEFAULT_CLIENT_INFO - - -class TraceServiceGrpcTransport(TraceServiceTransport): - """gRPC backend transport for TraceService. - - This file describes an API for collecting and viewing traces - and spans within a trace. A Trace is a collection of spans - corresponding to a single operation or set of operations for an - application. A span is an individual timed event which forms a - node of the trace tree. Spans for a single trace may span - multiple services. - - This class defines the same methods as the primary client, so the - primary client can load the underlying transport implementation - and call it. - - It sends protocol buffers over the wire using gRPC (which is built on - top of HTTP/2); the ``grpcio`` package must be installed. - """ - - _stubs: Dict[str, Callable] - - def __init__( - self, - *, - host: str = "cloudtrace.googleapis.com", - credentials: credentials.Credentials = None, - credentials_file: str = None, - scopes: Sequence[str] = None, - channel: grpc.Channel = None, - api_mtls_endpoint: str = None, - client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, - ssl_channel_credentials: grpc.ChannelCredentials = None, - client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, - quota_project_id: Optional[str] = None, - client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, - ) -> None: - """Instantiate the transport. - - Args: - host (Optional[str]): The hostname to connect to. - credentials (Optional[google.auth.credentials.Credentials]): The - authorization credentials to attach to requests. These - credentials identify the application to the service; if none - are specified, the client will attempt to ascertain the - credentials from the environment. - This argument is ignored if ``channel`` is provided. - credentials_file (Optional[str]): A file with credentials that can - be loaded with :func:`google.auth.load_credentials_from_file`. - This argument is ignored if ``channel`` is provided. - scopes (Optional(Sequence[str])): A list of scopes. This argument is - ignored if ``channel`` is provided. - channel (Optional[grpc.Channel]): A ``Channel`` instance through - which to make calls. - api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. - If provided, it overrides the ``host`` argument and tries to create - a mutual TLS channel with client SSL credentials from - ``client_cert_source`` or applicatin default SSL credentials. - client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): - Deprecated. A callback to provide client SSL certificate bytes and - private key bytes, both in PEM format. It is ignored if - ``api_mtls_endpoint`` is None. - ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials - for grpc channel. It is ignored if ``channel`` is provided. - client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): - A callback to provide client certificate bytes and private key bytes, - both in PEM format. It is used to configure mutual TLS channel. It is - ignored if ``channel`` or ``ssl_channel_credentials`` is provided. - quota_project_id (Optional[str]): An optional project to use for billing - and quota. - client_info (google.api_core.gapic_v1.client_info.ClientInfo): - The client info used to send a user-agent string along with - API requests. If ``None``, then default info will be used. - Generally, you only need to set this if you're developing - your own client library. - - Raises: - google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport - creation failed for any reason. - google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` - and ``credentials_file`` are passed. - """ - self._grpc_channel = None - self._ssl_channel_credentials = ssl_channel_credentials - self._stubs: Dict[str, Callable] = {} - - if api_mtls_endpoint: - warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) - if client_cert_source: - warnings.warn("client_cert_source is deprecated", DeprecationWarning) - - if channel: - # Ignore credentials if a channel was passed. - credentials = False - # If a channel was explicitly provided, set it. - self._grpc_channel = channel - self._ssl_channel_credentials = None - - else: - if api_mtls_endpoint: - host = api_mtls_endpoint - - # Create SSL credentials with client_cert_source or application - # default SSL credentials. - if client_cert_source: - cert, key = client_cert_source() - self._ssl_channel_credentials = grpc.ssl_channel_credentials( - certificate_chain=cert, private_key=key - ) - else: - self._ssl_channel_credentials = SslCredentials().ssl_credentials - - else: - if client_cert_source_for_mtls and not ssl_channel_credentials: - cert, key = client_cert_source_for_mtls() - self._ssl_channel_credentials = grpc.ssl_channel_credentials( - certificate_chain=cert, private_key=key - ) - - # The base transport sets the host, credentials and scopes - super().__init__( - host=host, - credentials=credentials, - credentials_file=credentials_file, - scopes=scopes, - quota_project_id=quota_project_id, - client_info=client_info, - ) - - if not self._grpc_channel: - self._grpc_channel = type(self).create_channel( - self._host, - credentials=self._credentials, - credentials_file=credentials_file, - scopes=self._scopes, - ssl_credentials=self._ssl_channel_credentials, - quota_project_id=quota_project_id, - options=[ - ("grpc.max_send_message_length", -1), - ("grpc.max_receive_message_length", -1), - ], - ) - - # Wrap messages. This must be done after self._grpc_channel exists - self._prep_wrapped_messages(client_info) - - @classmethod - def create_channel( - cls, - host: str = "cloudtrace.googleapis.com", - credentials: credentials.Credentials = None, - credentials_file: str = None, - scopes: Optional[Sequence[str]] = None, - quota_project_id: Optional[str] = None, - **kwargs, - ) -> grpc.Channel: - """Create and return a gRPC channel object. - Args: - host (Optional[str]): The host for the channel to use. - credentials (Optional[~.Credentials]): The - authorization credentials to attach to requests. These - credentials identify this application to the service. If - none are specified, the client will attempt to ascertain - the credentials from the environment. - credentials_file (Optional[str]): A file with credentials that can - be loaded with :func:`google.auth.load_credentials_from_file`. - This argument is mutually exclusive with credentials. - scopes (Optional[Sequence[str]]): A optional list of scopes needed for this - service. These are only used when credentials are not specified and - are passed to :func:`google.auth.default`. - quota_project_id (Optional[str]): An optional project to use for billing - and quota. - kwargs (Optional[dict]): Keyword arguments, which are passed to the - channel creation. - Returns: - grpc.Channel: A gRPC channel object. - - Raises: - google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` - and ``credentials_file`` are passed. - """ - scopes = scopes or cls.AUTH_SCOPES - return grpc_helpers.create_channel( - host, - credentials=credentials, - credentials_file=credentials_file, - scopes=scopes, - quota_project_id=quota_project_id, - **kwargs, - ) - - @property - def grpc_channel(self) -> grpc.Channel: - """Return the channel designed to connect to this service. - """ - return self._grpc_channel - - @property - def list_traces( - self, - ) -> Callable[[trace.ListTracesRequest], trace.ListTracesResponse]: - r"""Return a callable for the list traces method over gRPC. - - Returns of a list of traces that match the specified - filter conditions. - - Returns: - Callable[[~.ListTracesRequest], - ~.ListTracesResponse]: - A function that, when called, will call the underlying RPC - on the server. - """ - # Generate a "stub function" on-the-fly which will actually make - # the request. - # gRPC handles serialization and deserialization, so we just need - # to pass in the functions for each. - if "list_traces" not in self._stubs: - self._stubs["list_traces"] = self.grpc_channel.unary_unary( - "/google.devtools.cloudtrace.v1.TraceService/ListTraces", - request_serializer=trace.ListTracesRequest.serialize, - response_deserializer=trace.ListTracesResponse.deserialize, - ) - return self._stubs["list_traces"] - - @property - def get_trace(self) -> Callable[[trace.GetTraceRequest], trace.Trace]: - r"""Return a callable for the get trace method over gRPC. - - Gets a single trace by its ID. - - Returns: - Callable[[~.GetTraceRequest], - ~.Trace]: - A function that, when called, will call the underlying RPC - on the server. - """ - # Generate a "stub function" on-the-fly which will actually make - # the request. - # gRPC handles serialization and deserialization, so we just need - # to pass in the functions for each. - if "get_trace" not in self._stubs: - self._stubs["get_trace"] = self.grpc_channel.unary_unary( - "/google.devtools.cloudtrace.v1.TraceService/GetTrace", - request_serializer=trace.GetTraceRequest.serialize, - response_deserializer=trace.Trace.deserialize, - ) - return self._stubs["get_trace"] - - @property - def patch_traces(self) -> Callable[[trace.PatchTracesRequest], empty.Empty]: - r"""Return a callable for the patch traces method over gRPC. - - Sends new traces to Stackdriver Trace or updates - existing traces. If the ID of a trace that you send - matches that of an existing trace, any fields in the - existing trace and its spans are overwritten by the - provided values, and any new fields provided are merged - with the existing trace data. If the ID does not match, - a new trace is created. - - Returns: - Callable[[~.PatchTracesRequest], - ~.Empty]: - A function that, when called, will call the underlying RPC - on the server. - """ - # Generate a "stub function" on-the-fly which will actually make - # the request. - # gRPC handles serialization and deserialization, so we just need - # to pass in the functions for each. - if "patch_traces" not in self._stubs: - self._stubs["patch_traces"] = self.grpc_channel.unary_unary( - "/google.devtools.cloudtrace.v1.TraceService/PatchTraces", - request_serializer=trace.PatchTracesRequest.serialize, - response_deserializer=empty.Empty.FromString, - ) - return self._stubs["patch_traces"] - - -__all__ = ("TraceServiceGrpcTransport",) diff --git a/google/devtools/cloudtrace_v1/services/trace_service/transports/grpc_asyncio.py b/google/devtools/cloudtrace_v1/services/trace_service/transports/grpc_asyncio.py deleted file mode 100644 index c13c1125..00000000 --- a/google/devtools/cloudtrace_v1/services/trace_service/transports/grpc_asyncio.py +++ /dev/null @@ -1,318 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright 2020 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. -# - -import warnings -from typing import Awaitable, Callable, Dict, Optional, Sequence, Tuple - -from google.api_core import gapic_v1 # type: ignore -from google.api_core import grpc_helpers_async # type: ignore -from google import auth # type: ignore -from google.auth import credentials # type: ignore -from google.auth.transport.grpc import SslCredentials # type: ignore - -import grpc # type: ignore -from grpc.experimental import aio # type: ignore - -from google.devtools.cloudtrace_v1.types import trace -from google.protobuf import empty_pb2 as empty # type: ignore - -from .base import TraceServiceTransport, DEFAULT_CLIENT_INFO -from .grpc import TraceServiceGrpcTransport - - -class TraceServiceGrpcAsyncIOTransport(TraceServiceTransport): - """gRPC AsyncIO backend transport for TraceService. - - This file describes an API for collecting and viewing traces - and spans within a trace. A Trace is a collection of spans - corresponding to a single operation or set of operations for an - application. A span is an individual timed event which forms a - node of the trace tree. Spans for a single trace may span - multiple services. - - This class defines the same methods as the primary client, so the - primary client can load the underlying transport implementation - and call it. - - It sends protocol buffers over the wire using gRPC (which is built on - top of HTTP/2); the ``grpcio`` package must be installed. - """ - - _grpc_channel: aio.Channel - _stubs: Dict[str, Callable] = {} - - @classmethod - def create_channel( - cls, - host: str = "cloudtrace.googleapis.com", - credentials: credentials.Credentials = None, - credentials_file: Optional[str] = None, - scopes: Optional[Sequence[str]] = None, - quota_project_id: Optional[str] = None, - **kwargs, - ) -> aio.Channel: - """Create and return a gRPC AsyncIO channel object. - Args: - host (Optional[str]): The host for the channel to use. - credentials (Optional[~.Credentials]): The - authorization credentials to attach to requests. These - credentials identify this application to the service. If - none are specified, the client will attempt to ascertain - the credentials from the environment. - credentials_file (Optional[str]): A file with credentials that can - be loaded with :func:`google.auth.load_credentials_from_file`. - This argument is ignored if ``channel`` is provided. - scopes (Optional[Sequence[str]]): A optional list of scopes needed for this - service. These are only used when credentials are not specified and - are passed to :func:`google.auth.default`. - quota_project_id (Optional[str]): An optional project to use for billing - and quota. - kwargs (Optional[dict]): Keyword arguments, which are passed to the - channel creation. - Returns: - aio.Channel: A gRPC AsyncIO channel object. - """ - scopes = scopes or cls.AUTH_SCOPES - return grpc_helpers_async.create_channel( - host, - credentials=credentials, - credentials_file=credentials_file, - scopes=scopes, - quota_project_id=quota_project_id, - **kwargs, - ) - - def __init__( - self, - *, - host: str = "cloudtrace.googleapis.com", - credentials: credentials.Credentials = None, - credentials_file: Optional[str] = None, - scopes: Optional[Sequence[str]] = None, - channel: aio.Channel = None, - api_mtls_endpoint: str = None, - client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, - ssl_channel_credentials: grpc.ChannelCredentials = None, - client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, - quota_project_id=None, - client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, - ) -> None: - """Instantiate the transport. - - Args: - host (Optional[str]): The hostname to connect to. - credentials (Optional[google.auth.credentials.Credentials]): The - authorization credentials to attach to requests. These - credentials identify the application to the service; if none - are specified, the client will attempt to ascertain the - credentials from the environment. - This argument is ignored if ``channel`` is provided. - credentials_file (Optional[str]): A file with credentials that can - be loaded with :func:`google.auth.load_credentials_from_file`. - This argument is ignored if ``channel`` is provided. - scopes (Optional[Sequence[str]]): A optional list of scopes needed for this - service. These are only used when credentials are not specified and - are passed to :func:`google.auth.default`. - channel (Optional[aio.Channel]): A ``Channel`` instance through - which to make calls. - api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. - If provided, it overrides the ``host`` argument and tries to create - a mutual TLS channel with client SSL credentials from - ``client_cert_source`` or applicatin default SSL credentials. - client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): - Deprecated. A callback to provide client SSL certificate bytes and - private key bytes, both in PEM format. It is ignored if - ``api_mtls_endpoint`` is None. - ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials - for grpc channel. It is ignored if ``channel`` is provided. - client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): - A callback to provide client certificate bytes and private key bytes, - both in PEM format. It is used to configure mutual TLS channel. It is - ignored if ``channel`` or ``ssl_channel_credentials`` is provided. - quota_project_id (Optional[str]): An optional project to use for billing - and quota. - client_info (google.api_core.gapic_v1.client_info.ClientInfo): - The client info used to send a user-agent string along with - API requests. If ``None``, then default info will be used. - Generally, you only need to set this if you're developing - your own client library. - - Raises: - google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport - creation failed for any reason. - google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` - and ``credentials_file`` are passed. - """ - self._grpc_channel = None - self._ssl_channel_credentials = ssl_channel_credentials - self._stubs: Dict[str, Callable] = {} - - if api_mtls_endpoint: - warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) - if client_cert_source: - warnings.warn("client_cert_source is deprecated", DeprecationWarning) - - if channel: - # Ignore credentials if a channel was passed. - credentials = False - # If a channel was explicitly provided, set it. - self._grpc_channel = channel - self._ssl_channel_credentials = None - - else: - if api_mtls_endpoint: - host = api_mtls_endpoint - - # Create SSL credentials with client_cert_source or application - # default SSL credentials. - if client_cert_source: - cert, key = client_cert_source() - self._ssl_channel_credentials = grpc.ssl_channel_credentials( - certificate_chain=cert, private_key=key - ) - else: - self._ssl_channel_credentials = SslCredentials().ssl_credentials - - else: - if client_cert_source_for_mtls and not ssl_channel_credentials: - cert, key = client_cert_source_for_mtls() - self._ssl_channel_credentials = grpc.ssl_channel_credentials( - certificate_chain=cert, private_key=key - ) - - # The base transport sets the host, credentials and scopes - super().__init__( - host=host, - credentials=credentials, - credentials_file=credentials_file, - scopes=scopes, - quota_project_id=quota_project_id, - client_info=client_info, - ) - - if not self._grpc_channel: - self._grpc_channel = type(self).create_channel( - self._host, - credentials=self._credentials, - credentials_file=credentials_file, - scopes=self._scopes, - ssl_credentials=self._ssl_channel_credentials, - quota_project_id=quota_project_id, - options=[ - ("grpc.max_send_message_length", -1), - ("grpc.max_receive_message_length", -1), - ], - ) - - # Wrap messages. This must be done after self._grpc_channel exists - self._prep_wrapped_messages(client_info) - - @property - def grpc_channel(self) -> aio.Channel: - """Create the channel designed to connect to this service. - - This property caches on the instance; repeated calls return - the same channel. - """ - # Return the channel from cache. - return self._grpc_channel - - @property - def list_traces( - self, - ) -> Callable[[trace.ListTracesRequest], Awaitable[trace.ListTracesResponse]]: - r"""Return a callable for the list traces method over gRPC. - - Returns of a list of traces that match the specified - filter conditions. - - Returns: - Callable[[~.ListTracesRequest], - Awaitable[~.ListTracesResponse]]: - A function that, when called, will call the underlying RPC - on the server. - """ - # Generate a "stub function" on-the-fly which will actually make - # the request. - # gRPC handles serialization and deserialization, so we just need - # to pass in the functions for each. - if "list_traces" not in self._stubs: - self._stubs["list_traces"] = self.grpc_channel.unary_unary( - "/google.devtools.cloudtrace.v1.TraceService/ListTraces", - request_serializer=trace.ListTracesRequest.serialize, - response_deserializer=trace.ListTracesResponse.deserialize, - ) - return self._stubs["list_traces"] - - @property - def get_trace(self) -> Callable[[trace.GetTraceRequest], Awaitable[trace.Trace]]: - r"""Return a callable for the get trace method over gRPC. - - Gets a single trace by its ID. - - Returns: - Callable[[~.GetTraceRequest], - Awaitable[~.Trace]]: - A function that, when called, will call the underlying RPC - on the server. - """ - # Generate a "stub function" on-the-fly which will actually make - # the request. - # gRPC handles serialization and deserialization, so we just need - # to pass in the functions for each. - if "get_trace" not in self._stubs: - self._stubs["get_trace"] = self.grpc_channel.unary_unary( - "/google.devtools.cloudtrace.v1.TraceService/GetTrace", - request_serializer=trace.GetTraceRequest.serialize, - response_deserializer=trace.Trace.deserialize, - ) - return self._stubs["get_trace"] - - @property - def patch_traces( - self, - ) -> Callable[[trace.PatchTracesRequest], Awaitable[empty.Empty]]: - r"""Return a callable for the patch traces method over gRPC. - - Sends new traces to Stackdriver Trace or updates - existing traces. If the ID of a trace that you send - matches that of an existing trace, any fields in the - existing trace and its spans are overwritten by the - provided values, and any new fields provided are merged - with the existing trace data. If the ID does not match, - a new trace is created. - - Returns: - Callable[[~.PatchTracesRequest], - Awaitable[~.Empty]]: - A function that, when called, will call the underlying RPC - on the server. - """ - # Generate a "stub function" on-the-fly which will actually make - # the request. - # gRPC handles serialization and deserialization, so we just need - # to pass in the functions for each. - if "patch_traces" not in self._stubs: - self._stubs["patch_traces"] = self.grpc_channel.unary_unary( - "/google.devtools.cloudtrace.v1.TraceService/PatchTraces", - request_serializer=trace.PatchTracesRequest.serialize, - response_deserializer=empty.Empty.FromString, - ) - return self._stubs["patch_traces"] - - -__all__ = ("TraceServiceGrpcAsyncIOTransport",) diff --git a/google/devtools/cloudtrace_v1/types/__init__.py b/google/devtools/cloudtrace_v1/types/__init__.py deleted file mode 100644 index 540e3e00..00000000 --- a/google/devtools/cloudtrace_v1/types/__init__.py +++ /dev/null @@ -1,36 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright 2020 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. -# - -from .trace import ( - GetTraceRequest, - ListTracesRequest, - ListTracesResponse, - PatchTracesRequest, - Trace, - Traces, - TraceSpan, -) - -__all__ = ( - "GetTraceRequest", - "ListTracesRequest", - "ListTracesResponse", - "PatchTracesRequest", - "Trace", - "Traces", - "TraceSpan", -) diff --git a/google/devtools/cloudtrace_v1/types/trace.py b/google/devtools/cloudtrace_v1/types/trace.py deleted file mode 100644 index 1ef48db2..00000000 --- a/google/devtools/cloudtrace_v1/types/trace.py +++ /dev/null @@ -1,329 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright 2020 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. -# - -import proto # type: ignore - - -from google.protobuf import timestamp_pb2 as timestamp # type: ignore - - -__protobuf__ = proto.module( - package="google.devtools.cloudtrace.v1", - manifest={ - "Trace", - "Traces", - "TraceSpan", - "ListTracesRequest", - "ListTracesResponse", - "GetTraceRequest", - "PatchTracesRequest", - }, -) - - -class Trace(proto.Message): - r"""A trace describes how long it takes for an application to - perform an operation. It consists of a set of spans, each of - which represent a single timed event within the operation. - - Attributes: - project_id (str): - Project ID of the Cloud project where the - trace data is stored. - trace_id (str): - Globally unique identifier for the trace. This identifier is - a 128-bit numeric value formatted as a 32-byte hex string. - For example, ``382d4f4c6b7bb2f4a972559d9085001d``. - spans (Sequence[google.devtools.cloudtrace_v1.types.TraceSpan]): - Collection of spans in the trace. - """ - - project_id = proto.Field(proto.STRING, number=1) - - trace_id = proto.Field(proto.STRING, number=2) - - spans = proto.RepeatedField(proto.MESSAGE, number=3, message="TraceSpan",) - - -class Traces(proto.Message): - r"""List of new or updated traces. - - Attributes: - traces (Sequence[google.devtools.cloudtrace_v1.types.Trace]): - List of traces. - """ - - traces = proto.RepeatedField(proto.MESSAGE, number=1, message="Trace",) - - -class TraceSpan(proto.Message): - r"""A span represents a single timed event within a trace. Spans - can be nested and form a trace tree. Often, a trace contains a - root span that describes the end-to-end latency of an operation - and, optionally, one or more subspans for its suboperations. - Spans do not need to be contiguous. There may be gaps between - spans in a trace. - - Attributes: - span_id (int): - Identifier for the span. Must be a 64-bit integer other than - 0 and unique within a trace. For example, - ``2205310701640571284``. - kind (google.devtools.cloudtrace_v1.types.TraceSpan.SpanKind): - Distinguishes between spans generated in a particular - context. For example, two spans with the same name may be - distinguished using ``RPC_CLIENT`` and ``RPC_SERVER`` to - identify queueing latency associated with the span. - name (str): - Name of the span. Must be less than 128 - bytes. The span name is sanitized and displayed - in the Stackdriver Trace tool in the Google - Cloud Platform Console. - The name may be a method name or some other per- - call site name. For the same executable and the - same call point, a best practice is to use a - consistent name, which makes it easier to - correlate cross-trace spans. - start_time (google.protobuf.timestamp_pb2.Timestamp): - Start time of the span in nanoseconds from - the UNIX epoch. - end_time (google.protobuf.timestamp_pb2.Timestamp): - End time of the span in nanoseconds from the - UNIX epoch. - parent_span_id (int): - Optional. ID of the parent span, if any. - labels (Sequence[google.devtools.cloudtrace_v1.types.TraceSpan.LabelsEntry]): - Collection of labels associated with the span. Label keys - must be less than 128 bytes. Label values must be less than - 16 kilobytes (10MB for ``/stacktrace`` values). - - Some predefined label keys exist, or you may create your - own. When creating your own, we recommend the following - formats: - - - ``/category/product/key`` for agents of well-known - products (e.g. ``/db/mongodb/read_size``). - - ``short_host/path/key`` for domain-specific keys (e.g. - ``foo.com/myproduct/bar``) - - Predefined labels include: - - - ``/agent`` - - ``/component`` - - ``/error/message`` - - ``/error/name`` - - ``/http/client_city`` - - ``/http/client_country`` - - ``/http/client_protocol`` - - ``/http/client_region`` - - ``/http/host`` - - ``/http/method`` - - ``/http/path`` - - ``/http/redirected_url`` - - ``/http/request/size`` - - ``/http/response/size`` - - ``/http/route`` - - ``/http/status_code`` - - ``/http/url`` - - ``/http/user_agent`` - - ``/pid`` - - ``/stacktrace`` - - ``/tid`` - """ - - class SpanKind(proto.Enum): - r"""Type of span. Can be used to specify additional relationships - between spans in addition to a parent/child relationship. - """ - SPAN_KIND_UNSPECIFIED = 0 - RPC_SERVER = 1 - RPC_CLIENT = 2 - - span_id = proto.Field(proto.FIXED64, number=1) - - kind = proto.Field(proto.ENUM, number=2, enum=SpanKind,) - - name = proto.Field(proto.STRING, number=3) - - start_time = proto.Field(proto.MESSAGE, number=4, message=timestamp.Timestamp,) - - end_time = proto.Field(proto.MESSAGE, number=5, message=timestamp.Timestamp,) - - parent_span_id = proto.Field(proto.FIXED64, number=6) - - labels = proto.MapField(proto.STRING, proto.STRING, number=7) - - -class ListTracesRequest(proto.Message): - r"""The request message for the ``ListTraces`` method. All fields are - required unless specified. - - Attributes: - project_id (str): - Required. ID of the Cloud project where the - trace data is stored. - view (google.devtools.cloudtrace_v1.types.ListTracesRequest.ViewType): - Optional. Type of data returned for traces in the list. - Default is ``MINIMAL``. - page_size (int): - Optional. Maximum number of traces to return. - If not specified or <= 0, the implementation - selects a reasonable value. The implementation - may return fewer traces than the requested page - size. - page_token (str): - Token identifying the page of results to return. If - provided, use the value of the ``next_page_token`` field - from a previous request. - start_time (google.protobuf.timestamp_pb2.Timestamp): - Start of the time interval (inclusive) during - which the trace data was collected from the - application. - end_time (google.protobuf.timestamp_pb2.Timestamp): - End of the time interval (inclusive) during - which the trace data was collected from the - application. - filter (str): - Optional. A filter against labels for the request. - - By default, searches use prefix matching. To specify exact - match, prepend a plus symbol (``+``) to the search term. - Multiple terms are ANDed. Syntax: - - - ``root:NAME_PREFIX`` or ``NAME_PREFIX``: Return traces - where any root span starts with ``NAME_PREFIX``. - - ``+root:NAME`` or ``+NAME``: Return traces where any root - span's name is exactly ``NAME``. - - ``span:NAME_PREFIX``: Return traces where any span starts - with ``NAME_PREFIX``. - - ``+span:NAME``: Return traces where any span's name is - exactly ``NAME``. - - ``latency:DURATION``: Return traces whose overall latency - is greater or equal to than ``DURATION``. Accepted units - are nanoseconds (``ns``), milliseconds (``ms``), and - seconds (``s``). Default is ``ms``. For example, - ``latency:24ms`` returns traces whose overall latency is - greater than or equal to 24 milliseconds. - - ``label:LABEL_KEY``: Return all traces containing the - specified label key (exact match, case-sensitive) - regardless of the key:value pair's value (including empty - values). - - ``LABEL_KEY:VALUE_PREFIX``: Return all traces containing - the specified label key (exact match, case-sensitive) - whose value starts with ``VALUE_PREFIX``. Both a key and - a value must be specified. - - ``+LABEL_KEY:VALUE``: Return all traces containing a - key:value pair exactly matching the specified text. Both - a key and a value must be specified. - - ``method:VALUE``: Equivalent to ``/http/method:VALUE``. - - ``url:VALUE``: Equivalent to ``/http/url:VALUE``. - order_by (str): - Optional. Field used to sort the returned traces. Can be one - of the following: - - - ``trace_id`` - - ``name`` (``name`` field of root span in the trace) - - ``duration`` (difference between ``end_time`` and - ``start_time`` fields of the root span) - - ``start`` (``start_time`` field of the root span) - - Descending order can be specified by appending ``desc`` to - the sort field (for example, ``name desc``). - - Only one sort field is permitted. - """ - - class ViewType(proto.Enum): - r"""Type of data returned for traces in the list.""" - VIEW_TYPE_UNSPECIFIED = 0 - MINIMAL = 1 - ROOTSPAN = 2 - COMPLETE = 3 - - project_id = proto.Field(proto.STRING, number=1) - - view = proto.Field(proto.ENUM, number=2, enum=ViewType,) - - page_size = proto.Field(proto.INT32, number=3) - - page_token = proto.Field(proto.STRING, number=4) - - start_time = proto.Field(proto.MESSAGE, number=5, message=timestamp.Timestamp,) - - end_time = proto.Field(proto.MESSAGE, number=6, message=timestamp.Timestamp,) - - filter = proto.Field(proto.STRING, number=7) - - order_by = proto.Field(proto.STRING, number=8) - - -class ListTracesResponse(proto.Message): - r"""The response message for the ``ListTraces`` method. - - Attributes: - traces (Sequence[google.devtools.cloudtrace_v1.types.Trace]): - List of trace records as specified by the - view parameter. - next_page_token (str): - If defined, indicates that there are more - traces that match the request and that this - value should be passed to the next request to - continue retrieving additional traces. - """ - - @property - def raw_page(self): - return self - - traces = proto.RepeatedField(proto.MESSAGE, number=1, message="Trace",) - - next_page_token = proto.Field(proto.STRING, number=2) - - -class GetTraceRequest(proto.Message): - r"""The request message for the ``GetTrace`` method. - - Attributes: - project_id (str): - Required. ID of the Cloud project where the - trace data is stored. - trace_id (str): - Required. ID of the trace to return. - """ - - project_id = proto.Field(proto.STRING, number=1) - - trace_id = proto.Field(proto.STRING, number=2) - - -class PatchTracesRequest(proto.Message): - r"""The request message for the ``PatchTraces`` method. - - Attributes: - project_id (str): - Required. ID of the Cloud project where the - trace data is stored. - traces (google.devtools.cloudtrace_v1.types.Traces): - Required. The body of the message. - """ - - project_id = proto.Field(proto.STRING, number=1) - - traces = proto.Field(proto.MESSAGE, number=2, message="Traces",) - - -__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/devtools/cloudtrace_v2/py.typed b/google/devtools/cloudtrace_v2/py.typed deleted file mode 100644 index c9f021ea..00000000 --- a/google/devtools/cloudtrace_v2/py.typed +++ /dev/null @@ -1,2 +0,0 @@ -# Marker file for PEP 561. -# The google-devtools-cloudtrace package uses inline types. diff --git a/google/devtools/cloudtrace_v2/services/__init__.py b/google/devtools/cloudtrace_v2/services/__init__.py deleted file mode 100644 index 42ffdf2b..00000000 --- a/google/devtools/cloudtrace_v2/services/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright 2020 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. -# diff --git a/google/devtools/cloudtrace_v2/services/trace_service/__init__.py b/google/devtools/cloudtrace_v2/services/trace_service/__init__.py deleted file mode 100644 index e06e796c..00000000 --- a/google/devtools/cloudtrace_v2/services/trace_service/__init__.py +++ /dev/null @@ -1,24 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright 2020 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. -# - -from .client import TraceServiceClient -from .async_client import TraceServiceAsyncClient - -__all__ = ( - "TraceServiceClient", - "TraceServiceAsyncClient", -) diff --git a/google/devtools/cloudtrace_v2/services/trace_service/async_client.py b/google/devtools/cloudtrace_v2/services/trace_service/async_client.py deleted file mode 100644 index 2f60b822..00000000 --- a/google/devtools/cloudtrace_v2/services/trace_service/async_client.py +++ /dev/null @@ -1,341 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright 2020 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. -# - -from collections import OrderedDict -import functools -import re -from typing import Dict, Sequence, Tuple, Type, Union -import pkg_resources - -import google.api_core.client_options as ClientOptions # type: ignore -from google.api_core import exceptions # type: ignore -from google.api_core import gapic_v1 # type: ignore -from google.api_core import retry as retries # type: ignore -from google.auth import credentials # type: ignore -from google.oauth2 import service_account # type: ignore - -from google.devtools.cloudtrace_v2.types import trace -from google.devtools.cloudtrace_v2.types import tracing -from google.protobuf import timestamp_pb2 as timestamp # type: ignore -from google.protobuf import wrappers_pb2 as wrappers # type: ignore -from google.rpc import status_pb2 as status # type: ignore - -from .transports.base import TraceServiceTransport, DEFAULT_CLIENT_INFO -from .transports.grpc_asyncio import TraceServiceGrpcAsyncIOTransport -from .client import TraceServiceClient - - -class TraceServiceAsyncClient: - """This file describes an API for collecting and viewing traces - and spans within a trace. A Trace is a collection of spans - corresponding to a single operation or set of operations for an - application. A span is an individual timed event which forms a - node of the trace tree. A single trace may contain span(s) from - multiple services. - """ - - _client: TraceServiceClient - - DEFAULT_ENDPOINT = TraceServiceClient.DEFAULT_ENDPOINT - DEFAULT_MTLS_ENDPOINT = TraceServiceClient.DEFAULT_MTLS_ENDPOINT - - span_path = staticmethod(TraceServiceClient.span_path) - parse_span_path = staticmethod(TraceServiceClient.parse_span_path) - - common_billing_account_path = staticmethod( - TraceServiceClient.common_billing_account_path - ) - parse_common_billing_account_path = staticmethod( - TraceServiceClient.parse_common_billing_account_path - ) - - common_folder_path = staticmethod(TraceServiceClient.common_folder_path) - parse_common_folder_path = staticmethod(TraceServiceClient.parse_common_folder_path) - - common_organization_path = staticmethod(TraceServiceClient.common_organization_path) - parse_common_organization_path = staticmethod( - TraceServiceClient.parse_common_organization_path - ) - - common_project_path = staticmethod(TraceServiceClient.common_project_path) - parse_common_project_path = staticmethod( - TraceServiceClient.parse_common_project_path - ) - - common_location_path = staticmethod(TraceServiceClient.common_location_path) - parse_common_location_path = staticmethod( - TraceServiceClient.parse_common_location_path - ) - - @classmethod - def from_service_account_info(cls, info: dict, *args, **kwargs): - """Creates an instance of this client using the provided credentials info. - - Args: - info (dict): The service account private key info. - args: Additional arguments to pass to the constructor. - kwargs: Additional arguments to pass to the constructor. - - Returns: - TraceServiceAsyncClient: The constructed client. - """ - return TraceServiceClient.from_service_account_info.__func__(TraceServiceAsyncClient, info, *args, **kwargs) # type: ignore - - @classmethod - def from_service_account_file(cls, filename: str, *args, **kwargs): - """Creates an instance of this client using the provided credentials - file. - - Args: - filename (str): The path to the service account private key json - file. - args: Additional arguments to pass to the constructor. - kwargs: Additional arguments to pass to the constructor. - - Returns: - TraceServiceAsyncClient: The constructed client. - """ - return TraceServiceClient.from_service_account_file.__func__(TraceServiceAsyncClient, filename, *args, **kwargs) # type: ignore - - from_service_account_json = from_service_account_file - - @property - def transport(self) -> TraceServiceTransport: - """Return the transport used by the client instance. - - Returns: - TraceServiceTransport: The transport used by the client instance. - """ - return self._client.transport - - get_transport_class = functools.partial( - type(TraceServiceClient).get_transport_class, type(TraceServiceClient) - ) - - def __init__( - self, - *, - credentials: credentials.Credentials = None, - transport: Union[str, TraceServiceTransport] = "grpc_asyncio", - client_options: ClientOptions = None, - client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, - ) -> None: - """Instantiate the trace service client. - - Args: - credentials (Optional[google.auth.credentials.Credentials]): The - authorization credentials to attach to requests. These - credentials identify the application to the service; if none - are specified, the client will attempt to ascertain the - credentials from the environment. - transport (Union[str, ~.TraceServiceTransport]): The - transport to use. If set to None, a transport is chosen - automatically. - client_options (ClientOptions): Custom options for the client. It - won't take effect if a ``transport`` instance is provided. - (1) The ``api_endpoint`` property can be used to override the - default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT - environment variable can also be used to override the endpoint: - "always" (always use the default mTLS endpoint), "never" (always - use the default regular endpoint) and "auto" (auto switch to the - default mTLS endpoint if client certificate is present, this is - the default value). However, the ``api_endpoint`` property takes - precedence if provided. - (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable - is "true", then the ``client_cert_source`` property can be used - to provide client certificate for mutual TLS transport. If - not provided, the default SSL client certificate will be used if - present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not - set, no client certificate will be used. - - Raises: - google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport - creation failed for any reason. - """ - - self._client = TraceServiceClient( - credentials=credentials, - transport=transport, - client_options=client_options, - client_info=client_info, - ) - - async def batch_write_spans( - self, - request: tracing.BatchWriteSpansRequest = None, - *, - name: str = None, - spans: Sequence[trace.Span] = None, - retry: retries.Retry = gapic_v1.method.DEFAULT, - timeout: float = None, - metadata: Sequence[Tuple[str, str]] = (), - ) -> None: - r"""Sends new spans to new or existing traces. You cannot - update existing spans. - - Args: - request (:class:`google.devtools.cloudtrace_v2.types.BatchWriteSpansRequest`): - The request object. The request message for the - `BatchWriteSpans` method. - name (:class:`str`): - Required. The name of the project where the spans - belong. The format is ``projects/[PROJECT_ID]``. - - This corresponds to the ``name`` field - on the ``request`` instance; if ``request`` is provided, this - should not be set. - spans (:class:`Sequence[google.devtools.cloudtrace_v2.types.Span]`): - Required. A list of new spans. The - span names must not match existing - spans, or the results are undefined. - - This corresponds to the ``spans`` field - on the ``request`` instance; if ``request`` is provided, this - should not be set. - - retry (google.api_core.retry.Retry): Designation of what errors, if any, - should be retried. - timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. - """ - # Create or coerce a protobuf request object. - # Sanity check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. - has_flattened_params = any([name, spans]) - if request is not None and has_flattened_params: - raise ValueError( - "If the `request` argument is set, then none of " - "the individual field arguments should be set." - ) - - request = tracing.BatchWriteSpansRequest(request) - - # If we have keyword arguments corresponding to fields on the - # request, apply these. - - if name is not None: - request.name = name - - if spans: - request.spans.extend(spans) - - # Wrap the RPC method; this adds retry and timeout information, - # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.batch_write_spans, - default_timeout=120.0, - client_info=DEFAULT_CLIENT_INFO, - ) - - # Certain fields should be provided within the metadata header; - # add these here. - metadata = tuple(metadata) + ( - gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), - ) - - # Send the request. - await rpc( - request, retry=retry, timeout=timeout, metadata=metadata, - ) - - async def create_span( - self, - request: trace.Span = None, - *, - retry: retries.Retry = gapic_v1.method.DEFAULT, - timeout: float = None, - metadata: Sequence[Tuple[str, str]] = (), - ) -> trace.Span: - r"""Creates a new span. - - Args: - request (:class:`google.devtools.cloudtrace_v2.types.Span`): - The request object. A span represents a single operation - within a trace. Spans can be nested to form a trace - tree. Often, a trace contains a root span that describes - the end-to-end latency, and one or more subspans for its - sub-operations. A trace can also contain multiple root - spans, or none at all. Spans do not need to be - contiguous—there may be gaps or overlaps between - spans in a trace. - - retry (google.api_core.retry.Retry): Designation of what errors, if any, - should be retried. - timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. - - Returns: - google.devtools.cloudtrace_v2.types.Span: - A span represents a single operation - within a trace. Spans can be nested to - form a trace tree. Often, a trace - contains a root span that describes the - end-to-end latency, and one or more - subspans for its sub-operations. A trace - can also contain multiple root spans, or - none at all. Spans do not need to be - contiguous—there may be gaps or - overlaps between spans in a trace. - - """ - # Create or coerce a protobuf request object. - - request = trace.Span(request) - - # Wrap the RPC method; this adds retry and timeout information, - # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.create_span, - default_retry=retries.Retry( - initial=0.1, - maximum=1.0, - multiplier=1.2, - predicate=retries.if_exception_type( - exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, - ), - deadline=120.0, - ), - default_timeout=120.0, - client_info=DEFAULT_CLIENT_INFO, - ) - - # Certain fields should be provided within the metadata header; - # add these here. - metadata = tuple(metadata) + ( - gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), - ) - - # Send the request. - response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) - - # Done; return the response. - return response - - -try: - DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( - gapic_version=pkg_resources.get_distribution( - "google-devtools-cloudtrace", - ).version, - ) -except pkg_resources.DistributionNotFound: - DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() - - -__all__ = ("TraceServiceAsyncClient",) diff --git a/google/devtools/cloudtrace_v2/services/trace_service/client.py b/google/devtools/cloudtrace_v2/services/trace_service/client.py deleted file mode 100644 index 45762c7f..00000000 --- a/google/devtools/cloudtrace_v2/services/trace_service/client.py +++ /dev/null @@ -1,509 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright 2020 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. -# - -from collections import OrderedDict -from distutils import util -import os -import re -from typing import Callable, Dict, Optional, Sequence, Tuple, Type, Union -import pkg_resources - -from google.api_core import client_options as client_options_lib # type: ignore -from google.api_core import exceptions # type: ignore -from google.api_core import gapic_v1 # type: ignore -from google.api_core import retry as retries # type: ignore -from google.auth import credentials # type: ignore -from google.auth.transport import mtls # type: ignore -from google.auth.transport.grpc import SslCredentials # type: ignore -from google.auth.exceptions import MutualTLSChannelError # type: ignore -from google.oauth2 import service_account # type: ignore - -from google.devtools.cloudtrace_v2.types import trace -from google.devtools.cloudtrace_v2.types import tracing -from google.protobuf import timestamp_pb2 as timestamp # type: ignore -from google.protobuf import wrappers_pb2 as wrappers # type: ignore -from google.rpc import status_pb2 as status # type: ignore - -from .transports.base import TraceServiceTransport, DEFAULT_CLIENT_INFO -from .transports.grpc import TraceServiceGrpcTransport -from .transports.grpc_asyncio import TraceServiceGrpcAsyncIOTransport - - -class TraceServiceClientMeta(type): - """Metaclass for the TraceService client. - - This provides class-level methods for building and retrieving - support objects (e.g. transport) without polluting the client instance - objects. - """ - - _transport_registry = OrderedDict() # type: Dict[str, Type[TraceServiceTransport]] - _transport_registry["grpc"] = TraceServiceGrpcTransport - _transport_registry["grpc_asyncio"] = TraceServiceGrpcAsyncIOTransport - - def get_transport_class(cls, label: str = None,) -> Type[TraceServiceTransport]: - """Return an appropriate transport class. - - Args: - label: The name of the desired transport. If none is - provided, then the first transport in the registry is used. - - Returns: - The transport class to use. - """ - # If a specific transport is requested, return that one. - if label: - return cls._transport_registry[label] - - # No transport is requested; return the default (that is, the first one - # in the dictionary). - return next(iter(cls._transport_registry.values())) - - -class TraceServiceClient(metaclass=TraceServiceClientMeta): - """This file describes an API for collecting and viewing traces - and spans within a trace. A Trace is a collection of spans - corresponding to a single operation or set of operations for an - application. A span is an individual timed event which forms a - node of the trace tree. A single trace may contain span(s) from - multiple services. - """ - - @staticmethod - def _get_default_mtls_endpoint(api_endpoint): - """Convert api endpoint to mTLS endpoint. - Convert "*.sandbox.googleapis.com" and "*.googleapis.com" to - "*.mtls.sandbox.googleapis.com" and "*.mtls.googleapis.com" respectively. - Args: - api_endpoint (Optional[str]): the api endpoint to convert. - Returns: - str: converted mTLS api endpoint. - """ - if not api_endpoint: - return api_endpoint - - mtls_endpoint_re = re.compile( - r"(?P[^.]+)(?P\.mtls)?(?P\.sandbox)?(?P\.googleapis\.com)?" - ) - - m = mtls_endpoint_re.match(api_endpoint) - name, mtls, sandbox, googledomain = m.groups() - if mtls or not googledomain: - return api_endpoint - - if sandbox: - return api_endpoint.replace( - "sandbox.googleapis.com", "mtls.sandbox.googleapis.com" - ) - - return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com") - - DEFAULT_ENDPOINT = "cloudtrace.googleapis.com" - DEFAULT_MTLS_ENDPOINT = _get_default_mtls_endpoint.__func__( # type: ignore - DEFAULT_ENDPOINT - ) - - @classmethod - def from_service_account_info(cls, info: dict, *args, **kwargs): - """Creates an instance of this client using the provided credentials info. - - Args: - info (dict): The service account private key info. - args: Additional arguments to pass to the constructor. - kwargs: Additional arguments to pass to the constructor. - - Returns: - TraceServiceClient: The constructed client. - """ - credentials = service_account.Credentials.from_service_account_info(info) - kwargs["credentials"] = credentials - return cls(*args, **kwargs) - - @classmethod - def from_service_account_file(cls, filename: str, *args, **kwargs): - """Creates an instance of this client using the provided credentials - file. - - Args: - filename (str): The path to the service account private key json - file. - args: Additional arguments to pass to the constructor. - kwargs: Additional arguments to pass to the constructor. - - Returns: - TraceServiceClient: The constructed client. - """ - credentials = service_account.Credentials.from_service_account_file(filename) - kwargs["credentials"] = credentials - return cls(*args, **kwargs) - - from_service_account_json = from_service_account_file - - @property - def transport(self) -> TraceServiceTransport: - """Return the transport used by the client instance. - - Returns: - TraceServiceTransport: The transport used by the client instance. - """ - return self._transport - - @staticmethod - def span_path(project: str, trace: str, span: str,) -> str: - """Return a fully-qualified span string.""" - return "projects/{project}/traces/{trace}/spans/{span}".format( - project=project, trace=trace, span=span, - ) - - @staticmethod - def parse_span_path(path: str) -> Dict[str, str]: - """Parse a span path into its component segments.""" - m = re.match( - r"^projects/(?P.+?)/traces/(?P.+?)/spans/(?P.+?)$", - path, - ) - return m.groupdict() if m else {} - - @staticmethod - def common_billing_account_path(billing_account: str,) -> str: - """Return a fully-qualified billing_account string.""" - return "billingAccounts/{billing_account}".format( - billing_account=billing_account, - ) - - @staticmethod - def parse_common_billing_account_path(path: str) -> Dict[str, str]: - """Parse a billing_account path into its component segments.""" - m = re.match(r"^billingAccounts/(?P.+?)$", path) - return m.groupdict() if m else {} - - @staticmethod - def common_folder_path(folder: str,) -> str: - """Return a fully-qualified folder string.""" - return "folders/{folder}".format(folder=folder,) - - @staticmethod - def parse_common_folder_path(path: str) -> Dict[str, str]: - """Parse a folder path into its component segments.""" - m = re.match(r"^folders/(?P.+?)$", path) - return m.groupdict() if m else {} - - @staticmethod - def common_organization_path(organization: str,) -> str: - """Return a fully-qualified organization string.""" - return "organizations/{organization}".format(organization=organization,) - - @staticmethod - def parse_common_organization_path(path: str) -> Dict[str, str]: - """Parse a organization path into its component segments.""" - m = re.match(r"^organizations/(?P.+?)$", path) - return m.groupdict() if m else {} - - @staticmethod - def common_project_path(project: str,) -> str: - """Return a fully-qualified project string.""" - return "projects/{project}".format(project=project,) - - @staticmethod - def parse_common_project_path(path: str) -> Dict[str, str]: - """Parse a project path into its component segments.""" - m = re.match(r"^projects/(?P.+?)$", path) - return m.groupdict() if m else {} - - @staticmethod - def common_location_path(project: str, location: str,) -> str: - """Return a fully-qualified location string.""" - return "projects/{project}/locations/{location}".format( - project=project, location=location, - ) - - @staticmethod - def parse_common_location_path(path: str) -> Dict[str, str]: - """Parse a location path into its component segments.""" - m = re.match(r"^projects/(?P.+?)/locations/(?P.+?)$", path) - return m.groupdict() if m else {} - - def __init__( - self, - *, - credentials: Optional[credentials.Credentials] = None, - transport: Union[str, TraceServiceTransport, None] = None, - client_options: Optional[client_options_lib.ClientOptions] = None, - client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, - ) -> None: - """Instantiate the trace service client. - - Args: - credentials (Optional[google.auth.credentials.Credentials]): The - authorization credentials to attach to requests. These - credentials identify the application to the service; if none - are specified, the client will attempt to ascertain the - credentials from the environment. - transport (Union[str, TraceServiceTransport]): The - transport to use. If set to None, a transport is chosen - automatically. - client_options (google.api_core.client_options.ClientOptions): Custom options for the - client. It won't take effect if a ``transport`` instance is provided. - (1) The ``api_endpoint`` property can be used to override the - default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT - environment variable can also be used to override the endpoint: - "always" (always use the default mTLS endpoint), "never" (always - use the default regular endpoint) and "auto" (auto switch to the - default mTLS endpoint if client certificate is present, this is - the default value). However, the ``api_endpoint`` property takes - precedence if provided. - (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable - is "true", then the ``client_cert_source`` property can be used - to provide client certificate for mutual TLS transport. If - not provided, the default SSL client certificate will be used if - present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not - set, no client certificate will be used. - client_info (google.api_core.gapic_v1.client_info.ClientInfo): - The client info used to send a user-agent string along with - API requests. If ``None``, then default info will be used. - Generally, you only need to set this if you're developing - your own client library. - - Raises: - google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport - creation failed for any reason. - """ - if isinstance(client_options, dict): - client_options = client_options_lib.from_dict(client_options) - if client_options is None: - client_options = client_options_lib.ClientOptions() - - # Create SSL credentials for mutual TLS if needed. - use_client_cert = bool( - util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) - ) - - client_cert_source_func = None - is_mtls = False - if use_client_cert: - if client_options.client_cert_source: - is_mtls = True - client_cert_source_func = client_options.client_cert_source - else: - is_mtls = mtls.has_default_client_cert_source() - client_cert_source_func = ( - mtls.default_client_cert_source() if is_mtls else None - ) - - # Figure out which api endpoint to use. - if client_options.api_endpoint is not None: - api_endpoint = client_options.api_endpoint - else: - use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto") - if use_mtls_env == "never": - api_endpoint = self.DEFAULT_ENDPOINT - elif use_mtls_env == "always": - api_endpoint = self.DEFAULT_MTLS_ENDPOINT - elif use_mtls_env == "auto": - api_endpoint = ( - self.DEFAULT_MTLS_ENDPOINT if is_mtls else self.DEFAULT_ENDPOINT - ) - else: - raise MutualTLSChannelError( - "Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted values: never, auto, always" - ) - - # Save or instantiate the transport. - # Ordinarily, we provide the transport, but allowing a custom transport - # instance provides an extensibility point for unusual situations. - if isinstance(transport, TraceServiceTransport): - # transport is a TraceServiceTransport instance. - if credentials or client_options.credentials_file: - raise ValueError( - "When providing a transport instance, " - "provide its credentials directly." - ) - if client_options.scopes: - raise ValueError( - "When providing a transport instance, " - "provide its scopes directly." - ) - self._transport = transport - else: - Transport = type(self).get_transport_class(transport) - self._transport = Transport( - credentials=credentials, - credentials_file=client_options.credentials_file, - host=api_endpoint, - scopes=client_options.scopes, - client_cert_source_for_mtls=client_cert_source_func, - quota_project_id=client_options.quota_project_id, - client_info=client_info, - ) - - def batch_write_spans( - self, - request: tracing.BatchWriteSpansRequest = None, - *, - name: str = None, - spans: Sequence[trace.Span] = None, - retry: retries.Retry = gapic_v1.method.DEFAULT, - timeout: float = None, - metadata: Sequence[Tuple[str, str]] = (), - ) -> None: - r"""Sends new spans to new or existing traces. You cannot - update existing spans. - - Args: - request (google.devtools.cloudtrace_v2.types.BatchWriteSpansRequest): - The request object. The request message for the - `BatchWriteSpans` method. - name (str): - Required. The name of the project where the spans - belong. The format is ``projects/[PROJECT_ID]``. - - This corresponds to the ``name`` field - on the ``request`` instance; if ``request`` is provided, this - should not be set. - spans (Sequence[google.devtools.cloudtrace_v2.types.Span]): - Required. A list of new spans. The - span names must not match existing - spans, or the results are undefined. - - This corresponds to the ``spans`` field - on the ``request`` instance; if ``request`` is provided, this - should not be set. - - retry (google.api_core.retry.Retry): Designation of what errors, if any, - should be retried. - timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. - """ - # Create or coerce a protobuf request object. - # Sanity check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. - has_flattened_params = any([name, spans]) - if request is not None and has_flattened_params: - raise ValueError( - "If the `request` argument is set, then none of " - "the individual field arguments should be set." - ) - - # Minor optimization to avoid making a copy if the user passes - # in a tracing.BatchWriteSpansRequest. - # There's no risk of modifying the input as we've already verified - # there are no flattened fields. - if not isinstance(request, tracing.BatchWriteSpansRequest): - request = tracing.BatchWriteSpansRequest(request) - - # If we have keyword arguments corresponding to fields on the - # request, apply these. - - if name is not None: - request.name = name - if spans is not None: - request.spans = spans - - # Wrap the RPC method; this adds retry and timeout information, - # and friendly error handling. - rpc = self._transport._wrapped_methods[self._transport.batch_write_spans] - - # Certain fields should be provided within the metadata header; - # add these here. - metadata = tuple(metadata) + ( - gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), - ) - - # Send the request. - rpc( - request, retry=retry, timeout=timeout, metadata=metadata, - ) - - def create_span( - self, - request: trace.Span = None, - *, - retry: retries.Retry = gapic_v1.method.DEFAULT, - timeout: float = None, - metadata: Sequence[Tuple[str, str]] = (), - ) -> trace.Span: - r"""Creates a new span. - - Args: - request (google.devtools.cloudtrace_v2.types.Span): - The request object. A span represents a single operation - within a trace. Spans can be nested to form a trace - tree. Often, a trace contains a root span that describes - the end-to-end latency, and one or more subspans for its - sub-operations. A trace can also contain multiple root - spans, or none at all. Spans do not need to be - contiguous—there may be gaps or overlaps between - spans in a trace. - - retry (google.api_core.retry.Retry): Designation of what errors, if any, - should be retried. - timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. - - Returns: - google.devtools.cloudtrace_v2.types.Span: - A span represents a single operation - within a trace. Spans can be nested to - form a trace tree. Often, a trace - contains a root span that describes the - end-to-end latency, and one or more - subspans for its sub-operations. A trace - can also contain multiple root spans, or - none at all. Spans do not need to be - contiguous—there may be gaps or - overlaps between spans in a trace. - - """ - # Create or coerce a protobuf request object. - - # Minor optimization to avoid making a copy if the user passes - # in a trace.Span. - # There's no risk of modifying the input as we've already verified - # there are no flattened fields. - if not isinstance(request, trace.Span): - request = trace.Span(request) - - # Wrap the RPC method; this adds retry and timeout information, - # and friendly error handling. - rpc = self._transport._wrapped_methods[self._transport.create_span] - - # Certain fields should be provided within the metadata header; - # add these here. - metadata = tuple(metadata) + ( - gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), - ) - - # Send the request. - response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) - - # Done; return the response. - return response - - -try: - DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( - gapic_version=pkg_resources.get_distribution( - "google-devtools-cloudtrace", - ).version, - ) -except pkg_resources.DistributionNotFound: - DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() - - -__all__ = ("TraceServiceClient",) diff --git a/google/devtools/cloudtrace_v2/services/trace_service/transports/__init__.py b/google/devtools/cloudtrace_v2/services/trace_service/transports/__init__.py deleted file mode 100644 index b860866b..00000000 --- a/google/devtools/cloudtrace_v2/services/trace_service/transports/__init__.py +++ /dev/null @@ -1,35 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright 2020 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. -# - -from collections import OrderedDict -from typing import Dict, Type - -from .base import TraceServiceTransport -from .grpc import TraceServiceGrpcTransport -from .grpc_asyncio import TraceServiceGrpcAsyncIOTransport - - -# Compile a registry of transports. -_transport_registry = OrderedDict() # type: Dict[str, Type[TraceServiceTransport]] -_transport_registry["grpc"] = TraceServiceGrpcTransport -_transport_registry["grpc_asyncio"] = TraceServiceGrpcAsyncIOTransport - -__all__ = ( - "TraceServiceTransport", - "TraceServiceGrpcTransport", - "TraceServiceGrpcAsyncIOTransport", -) diff --git a/google/devtools/cloudtrace_v2/services/trace_service/transports/base.py b/google/devtools/cloudtrace_v2/services/trace_service/transports/base.py deleted file mode 100644 index 5a934424..00000000 --- a/google/devtools/cloudtrace_v2/services/trace_service/transports/base.py +++ /dev/null @@ -1,151 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright 2020 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. -# - -import abc -import typing -import pkg_resources - -from google import auth # type: ignore -from google.api_core import exceptions # type: ignore -from google.api_core import gapic_v1 # type: ignore -from google.api_core import retry as retries # type: ignore -from google.auth import credentials # type: ignore - -from google.devtools.cloudtrace_v2.types import trace -from google.devtools.cloudtrace_v2.types import tracing -from google.protobuf import empty_pb2 as empty # type: ignore - - -try: - DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( - gapic_version=pkg_resources.get_distribution( - "google-devtools-cloudtrace", - ).version, - ) -except pkg_resources.DistributionNotFound: - DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() - - -class TraceServiceTransport(abc.ABC): - """Abstract transport class for TraceService.""" - - AUTH_SCOPES = ( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/trace.append", - ) - - def __init__( - self, - *, - host: str = "cloudtrace.googleapis.com", - credentials: credentials.Credentials = None, - credentials_file: typing.Optional[str] = None, - scopes: typing.Optional[typing.Sequence[str]] = AUTH_SCOPES, - quota_project_id: typing.Optional[str] = None, - client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, - **kwargs, - ) -> None: - """Instantiate the transport. - - Args: - host (Optional[str]): The hostname to connect to. - credentials (Optional[google.auth.credentials.Credentials]): The - authorization credentials to attach to requests. These - credentials identify the application to the service; if none - are specified, the client will attempt to ascertain the - credentials from the environment. - credentials_file (Optional[str]): A file with credentials that can - be loaded with :func:`google.auth.load_credentials_from_file`. - This argument is mutually exclusive with credentials. - scope (Optional[Sequence[str]]): A list of scopes. - quota_project_id (Optional[str]): An optional project to use for billing - and quota. - client_info (google.api_core.gapic_v1.client_info.ClientInfo): - The client info used to send a user-agent string along with - API requests. If ``None``, then default info will be used. - Generally, you only need to set this if you're developing - your own client library. - """ - # Save the hostname. Default to port 443 (HTTPS) if none is specified. - if ":" not in host: - host += ":443" - self._host = host - - # Save the scopes. - self._scopes = scopes or self.AUTH_SCOPES - - # If no credentials are provided, then determine the appropriate - # defaults. - if credentials and credentials_file: - raise exceptions.DuplicateCredentialArgs( - "'credentials_file' and 'credentials' are mutually exclusive" - ) - - if credentials_file is not None: - credentials, _ = auth.load_credentials_from_file( - credentials_file, scopes=self._scopes, quota_project_id=quota_project_id - ) - - elif credentials is None: - credentials, _ = auth.default( - scopes=self._scopes, quota_project_id=quota_project_id - ) - - # Save the credentials. - self._credentials = credentials - - def _prep_wrapped_messages(self, client_info): - # Precompute the wrapped methods. - self._wrapped_methods = { - self.batch_write_spans: gapic_v1.method.wrap_method( - self.batch_write_spans, default_timeout=120.0, client_info=client_info, - ), - self.create_span: gapic_v1.method.wrap_method( - self.create_span, - default_retry=retries.Retry( - initial=0.1, - maximum=1.0, - multiplier=1.2, - predicate=retries.if_exception_type( - exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, - ), - deadline=120.0, - ), - default_timeout=120.0, - client_info=client_info, - ), - } - - @property - def batch_write_spans( - self, - ) -> typing.Callable[ - [tracing.BatchWriteSpansRequest], - typing.Union[empty.Empty, typing.Awaitable[empty.Empty]], - ]: - raise NotImplementedError() - - @property - def create_span( - self, - ) -> typing.Callable[ - [trace.Span], typing.Union[trace.Span, typing.Awaitable[trace.Span]] - ]: - raise NotImplementedError() - - -__all__ = ("TraceServiceTransport",) diff --git a/google/devtools/cloudtrace_v2/services/trace_service/transports/grpc.py b/google/devtools/cloudtrace_v2/services/trace_service/transports/grpc.py deleted file mode 100644 index 2cd5e147..00000000 --- a/google/devtools/cloudtrace_v2/services/trace_service/transports/grpc.py +++ /dev/null @@ -1,283 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright 2020 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. -# - -import warnings -from typing import Callable, Dict, Optional, Sequence, Tuple - -from google.api_core import grpc_helpers # type: ignore -from google.api_core import gapic_v1 # type: ignore -from google import auth # type: ignore -from google.auth import credentials # type: ignore -from google.auth.transport.grpc import SslCredentials # type: ignore - -import grpc # type: ignore - -from google.devtools.cloudtrace_v2.types import trace -from google.devtools.cloudtrace_v2.types import tracing -from google.protobuf import empty_pb2 as empty # type: ignore - -from .base import TraceServiceTransport, DEFAULT_CLIENT_INFO - - -class TraceServiceGrpcTransport(TraceServiceTransport): - """gRPC backend transport for TraceService. - - This file describes an API for collecting and viewing traces - and spans within a trace. A Trace is a collection of spans - corresponding to a single operation or set of operations for an - application. A span is an individual timed event which forms a - node of the trace tree. A single trace may contain span(s) from - multiple services. - - This class defines the same methods as the primary client, so the - primary client can load the underlying transport implementation - and call it. - - It sends protocol buffers over the wire using gRPC (which is built on - top of HTTP/2); the ``grpcio`` package must be installed. - """ - - _stubs: Dict[str, Callable] - - def __init__( - self, - *, - host: str = "cloudtrace.googleapis.com", - credentials: credentials.Credentials = None, - credentials_file: str = None, - scopes: Sequence[str] = None, - channel: grpc.Channel = None, - api_mtls_endpoint: str = None, - client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, - ssl_channel_credentials: grpc.ChannelCredentials = None, - client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, - quota_project_id: Optional[str] = None, - client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, - ) -> None: - """Instantiate the transport. - - Args: - host (Optional[str]): The hostname to connect to. - credentials (Optional[google.auth.credentials.Credentials]): The - authorization credentials to attach to requests. These - credentials identify the application to the service; if none - are specified, the client will attempt to ascertain the - credentials from the environment. - This argument is ignored if ``channel`` is provided. - credentials_file (Optional[str]): A file with credentials that can - be loaded with :func:`google.auth.load_credentials_from_file`. - This argument is ignored if ``channel`` is provided. - scopes (Optional(Sequence[str])): A list of scopes. This argument is - ignored if ``channel`` is provided. - channel (Optional[grpc.Channel]): A ``Channel`` instance through - which to make calls. - api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. - If provided, it overrides the ``host`` argument and tries to create - a mutual TLS channel with client SSL credentials from - ``client_cert_source`` or applicatin default SSL credentials. - client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): - Deprecated. A callback to provide client SSL certificate bytes and - private key bytes, both in PEM format. It is ignored if - ``api_mtls_endpoint`` is None. - ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials - for grpc channel. It is ignored if ``channel`` is provided. - client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): - A callback to provide client certificate bytes and private key bytes, - both in PEM format. It is used to configure mutual TLS channel. It is - ignored if ``channel`` or ``ssl_channel_credentials`` is provided. - quota_project_id (Optional[str]): An optional project to use for billing - and quota. - client_info (google.api_core.gapic_v1.client_info.ClientInfo): - The client info used to send a user-agent string along with - API requests. If ``None``, then default info will be used. - Generally, you only need to set this if you're developing - your own client library. - - Raises: - google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport - creation failed for any reason. - google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` - and ``credentials_file`` are passed. - """ - self._grpc_channel = None - self._ssl_channel_credentials = ssl_channel_credentials - self._stubs: Dict[str, Callable] = {} - - if api_mtls_endpoint: - warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) - if client_cert_source: - warnings.warn("client_cert_source is deprecated", DeprecationWarning) - - if channel: - # Ignore credentials if a channel was passed. - credentials = False - # If a channel was explicitly provided, set it. - self._grpc_channel = channel - self._ssl_channel_credentials = None - - else: - if api_mtls_endpoint: - host = api_mtls_endpoint - - # Create SSL credentials with client_cert_source or application - # default SSL credentials. - if client_cert_source: - cert, key = client_cert_source() - self._ssl_channel_credentials = grpc.ssl_channel_credentials( - certificate_chain=cert, private_key=key - ) - else: - self._ssl_channel_credentials = SslCredentials().ssl_credentials - - else: - if client_cert_source_for_mtls and not ssl_channel_credentials: - cert, key = client_cert_source_for_mtls() - self._ssl_channel_credentials = grpc.ssl_channel_credentials( - certificate_chain=cert, private_key=key - ) - - # The base transport sets the host, credentials and scopes - super().__init__( - host=host, - credentials=credentials, - credentials_file=credentials_file, - scopes=scopes, - quota_project_id=quota_project_id, - client_info=client_info, - ) - - if not self._grpc_channel: - self._grpc_channel = type(self).create_channel( - self._host, - credentials=self._credentials, - credentials_file=credentials_file, - scopes=self._scopes, - ssl_credentials=self._ssl_channel_credentials, - quota_project_id=quota_project_id, - options=[ - ("grpc.max_send_message_length", -1), - ("grpc.max_receive_message_length", -1), - ], - ) - - # Wrap messages. This must be done after self._grpc_channel exists - self._prep_wrapped_messages(client_info) - - @classmethod - def create_channel( - cls, - host: str = "cloudtrace.googleapis.com", - credentials: credentials.Credentials = None, - credentials_file: str = None, - scopes: Optional[Sequence[str]] = None, - quota_project_id: Optional[str] = None, - **kwargs, - ) -> grpc.Channel: - """Create and return a gRPC channel object. - Args: - host (Optional[str]): The host for the channel to use. - credentials (Optional[~.Credentials]): The - authorization credentials to attach to requests. These - credentials identify this application to the service. If - none are specified, the client will attempt to ascertain - the credentials from the environment. - credentials_file (Optional[str]): A file with credentials that can - be loaded with :func:`google.auth.load_credentials_from_file`. - This argument is mutually exclusive with credentials. - scopes (Optional[Sequence[str]]): A optional list of scopes needed for this - service. These are only used when credentials are not specified and - are passed to :func:`google.auth.default`. - quota_project_id (Optional[str]): An optional project to use for billing - and quota. - kwargs (Optional[dict]): Keyword arguments, which are passed to the - channel creation. - Returns: - grpc.Channel: A gRPC channel object. - - Raises: - google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` - and ``credentials_file`` are passed. - """ - scopes = scopes or cls.AUTH_SCOPES - return grpc_helpers.create_channel( - host, - credentials=credentials, - credentials_file=credentials_file, - scopes=scopes, - quota_project_id=quota_project_id, - **kwargs, - ) - - @property - def grpc_channel(self) -> grpc.Channel: - """Return the channel designed to connect to this service. - """ - return self._grpc_channel - - @property - def batch_write_spans( - self, - ) -> Callable[[tracing.BatchWriteSpansRequest], empty.Empty]: - r"""Return a callable for the batch write spans method over gRPC. - - Sends new spans to new or existing traces. You cannot - update existing spans. - - Returns: - Callable[[~.BatchWriteSpansRequest], - ~.Empty]: - A function that, when called, will call the underlying RPC - on the server. - """ - # Generate a "stub function" on-the-fly which will actually make - # the request. - # gRPC handles serialization and deserialization, so we just need - # to pass in the functions for each. - if "batch_write_spans" not in self._stubs: - self._stubs["batch_write_spans"] = self.grpc_channel.unary_unary( - "/google.devtools.cloudtrace.v2.TraceService/BatchWriteSpans", - request_serializer=tracing.BatchWriteSpansRequest.serialize, - response_deserializer=empty.Empty.FromString, - ) - return self._stubs["batch_write_spans"] - - @property - def create_span(self) -> Callable[[trace.Span], trace.Span]: - r"""Return a callable for the create span method over gRPC. - - Creates a new span. - - Returns: - Callable[[~.Span], - ~.Span]: - A function that, when called, will call the underlying RPC - on the server. - """ - # Generate a "stub function" on-the-fly which will actually make - # the request. - # gRPC handles serialization and deserialization, so we just need - # to pass in the functions for each. - if "create_span" not in self._stubs: - self._stubs["create_span"] = self.grpc_channel.unary_unary( - "/google.devtools.cloudtrace.v2.TraceService/CreateSpan", - request_serializer=trace.Span.serialize, - response_deserializer=trace.Span.deserialize, - ) - return self._stubs["create_span"] - - -__all__ = ("TraceServiceGrpcTransport",) diff --git a/google/devtools/cloudtrace_v2/services/trace_service/transports/grpc_asyncio.py b/google/devtools/cloudtrace_v2/services/trace_service/transports/grpc_asyncio.py deleted file mode 100644 index 68bc1b03..00000000 --- a/google/devtools/cloudtrace_v2/services/trace_service/transports/grpc_asyncio.py +++ /dev/null @@ -1,287 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright 2020 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. -# - -import warnings -from typing import Awaitable, Callable, Dict, Optional, Sequence, Tuple - -from google.api_core import gapic_v1 # type: ignore -from google.api_core import grpc_helpers_async # type: ignore -from google import auth # type: ignore -from google.auth import credentials # type: ignore -from google.auth.transport.grpc import SslCredentials # type: ignore - -import grpc # type: ignore -from grpc.experimental import aio # type: ignore - -from google.devtools.cloudtrace_v2.types import trace -from google.devtools.cloudtrace_v2.types import tracing -from google.protobuf import empty_pb2 as empty # type: ignore - -from .base import TraceServiceTransport, DEFAULT_CLIENT_INFO -from .grpc import TraceServiceGrpcTransport - - -class TraceServiceGrpcAsyncIOTransport(TraceServiceTransport): - """gRPC AsyncIO backend transport for TraceService. - - This file describes an API for collecting and viewing traces - and spans within a trace. A Trace is a collection of spans - corresponding to a single operation or set of operations for an - application. A span is an individual timed event which forms a - node of the trace tree. A single trace may contain span(s) from - multiple services. - - This class defines the same methods as the primary client, so the - primary client can load the underlying transport implementation - and call it. - - It sends protocol buffers over the wire using gRPC (which is built on - top of HTTP/2); the ``grpcio`` package must be installed. - """ - - _grpc_channel: aio.Channel - _stubs: Dict[str, Callable] = {} - - @classmethod - def create_channel( - cls, - host: str = "cloudtrace.googleapis.com", - credentials: credentials.Credentials = None, - credentials_file: Optional[str] = None, - scopes: Optional[Sequence[str]] = None, - quota_project_id: Optional[str] = None, - **kwargs, - ) -> aio.Channel: - """Create and return a gRPC AsyncIO channel object. - Args: - host (Optional[str]): The host for the channel to use. - credentials (Optional[~.Credentials]): The - authorization credentials to attach to requests. These - credentials identify this application to the service. If - none are specified, the client will attempt to ascertain - the credentials from the environment. - credentials_file (Optional[str]): A file with credentials that can - be loaded with :func:`google.auth.load_credentials_from_file`. - This argument is ignored if ``channel`` is provided. - scopes (Optional[Sequence[str]]): A optional list of scopes needed for this - service. These are only used when credentials are not specified and - are passed to :func:`google.auth.default`. - quota_project_id (Optional[str]): An optional project to use for billing - and quota. - kwargs (Optional[dict]): Keyword arguments, which are passed to the - channel creation. - Returns: - aio.Channel: A gRPC AsyncIO channel object. - """ - scopes = scopes or cls.AUTH_SCOPES - return grpc_helpers_async.create_channel( - host, - credentials=credentials, - credentials_file=credentials_file, - scopes=scopes, - quota_project_id=quota_project_id, - **kwargs, - ) - - def __init__( - self, - *, - host: str = "cloudtrace.googleapis.com", - credentials: credentials.Credentials = None, - credentials_file: Optional[str] = None, - scopes: Optional[Sequence[str]] = None, - channel: aio.Channel = None, - api_mtls_endpoint: str = None, - client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, - ssl_channel_credentials: grpc.ChannelCredentials = None, - client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, - quota_project_id=None, - client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, - ) -> None: - """Instantiate the transport. - - Args: - host (Optional[str]): The hostname to connect to. - credentials (Optional[google.auth.credentials.Credentials]): The - authorization credentials to attach to requests. These - credentials identify the application to the service; if none - are specified, the client will attempt to ascertain the - credentials from the environment. - This argument is ignored if ``channel`` is provided. - credentials_file (Optional[str]): A file with credentials that can - be loaded with :func:`google.auth.load_credentials_from_file`. - This argument is ignored if ``channel`` is provided. - scopes (Optional[Sequence[str]]): A optional list of scopes needed for this - service. These are only used when credentials are not specified and - are passed to :func:`google.auth.default`. - channel (Optional[aio.Channel]): A ``Channel`` instance through - which to make calls. - api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. - If provided, it overrides the ``host`` argument and tries to create - a mutual TLS channel with client SSL credentials from - ``client_cert_source`` or applicatin default SSL credentials. - client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): - Deprecated. A callback to provide client SSL certificate bytes and - private key bytes, both in PEM format. It is ignored if - ``api_mtls_endpoint`` is None. - ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials - for grpc channel. It is ignored if ``channel`` is provided. - client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): - A callback to provide client certificate bytes and private key bytes, - both in PEM format. It is used to configure mutual TLS channel. It is - ignored if ``channel`` or ``ssl_channel_credentials`` is provided. - quota_project_id (Optional[str]): An optional project to use for billing - and quota. - client_info (google.api_core.gapic_v1.client_info.ClientInfo): - The client info used to send a user-agent string along with - API requests. If ``None``, then default info will be used. - Generally, you only need to set this if you're developing - your own client library. - - Raises: - google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport - creation failed for any reason. - google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` - and ``credentials_file`` are passed. - """ - self._grpc_channel = None - self._ssl_channel_credentials = ssl_channel_credentials - self._stubs: Dict[str, Callable] = {} - - if api_mtls_endpoint: - warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) - if client_cert_source: - warnings.warn("client_cert_source is deprecated", DeprecationWarning) - - if channel: - # Ignore credentials if a channel was passed. - credentials = False - # If a channel was explicitly provided, set it. - self._grpc_channel = channel - self._ssl_channel_credentials = None - - else: - if api_mtls_endpoint: - host = api_mtls_endpoint - - # Create SSL credentials with client_cert_source or application - # default SSL credentials. - if client_cert_source: - cert, key = client_cert_source() - self._ssl_channel_credentials = grpc.ssl_channel_credentials( - certificate_chain=cert, private_key=key - ) - else: - self._ssl_channel_credentials = SslCredentials().ssl_credentials - - else: - if client_cert_source_for_mtls and not ssl_channel_credentials: - cert, key = client_cert_source_for_mtls() - self._ssl_channel_credentials = grpc.ssl_channel_credentials( - certificate_chain=cert, private_key=key - ) - - # The base transport sets the host, credentials and scopes - super().__init__( - host=host, - credentials=credentials, - credentials_file=credentials_file, - scopes=scopes, - quota_project_id=quota_project_id, - client_info=client_info, - ) - - if not self._grpc_channel: - self._grpc_channel = type(self).create_channel( - self._host, - credentials=self._credentials, - credentials_file=credentials_file, - scopes=self._scopes, - ssl_credentials=self._ssl_channel_credentials, - quota_project_id=quota_project_id, - options=[ - ("grpc.max_send_message_length", -1), - ("grpc.max_receive_message_length", -1), - ], - ) - - # Wrap messages. This must be done after self._grpc_channel exists - self._prep_wrapped_messages(client_info) - - @property - def grpc_channel(self) -> aio.Channel: - """Create the channel designed to connect to this service. - - This property caches on the instance; repeated calls return - the same channel. - """ - # Return the channel from cache. - return self._grpc_channel - - @property - def batch_write_spans( - self, - ) -> Callable[[tracing.BatchWriteSpansRequest], Awaitable[empty.Empty]]: - r"""Return a callable for the batch write spans method over gRPC. - - Sends new spans to new or existing traces. You cannot - update existing spans. - - Returns: - Callable[[~.BatchWriteSpansRequest], - Awaitable[~.Empty]]: - A function that, when called, will call the underlying RPC - on the server. - """ - # Generate a "stub function" on-the-fly which will actually make - # the request. - # gRPC handles serialization and deserialization, so we just need - # to pass in the functions for each. - if "batch_write_spans" not in self._stubs: - self._stubs["batch_write_spans"] = self.grpc_channel.unary_unary( - "/google.devtools.cloudtrace.v2.TraceService/BatchWriteSpans", - request_serializer=tracing.BatchWriteSpansRequest.serialize, - response_deserializer=empty.Empty.FromString, - ) - return self._stubs["batch_write_spans"] - - @property - def create_span(self) -> Callable[[trace.Span], Awaitable[trace.Span]]: - r"""Return a callable for the create span method over gRPC. - - Creates a new span. - - Returns: - Callable[[~.Span], - Awaitable[~.Span]]: - A function that, when called, will call the underlying RPC - on the server. - """ - # Generate a "stub function" on-the-fly which will actually make - # the request. - # gRPC handles serialization and deserialization, so we just need - # to pass in the functions for each. - if "create_span" not in self._stubs: - self._stubs["create_span"] = self.grpc_channel.unary_unary( - "/google.devtools.cloudtrace.v2.TraceService/CreateSpan", - request_serializer=trace.Span.serialize, - response_deserializer=trace.Span.deserialize, - ) - return self._stubs["create_span"] - - -__all__ = ("TraceServiceGrpcAsyncIOTransport",) diff --git a/google/devtools/cloudtrace_v2/types/__init__.py b/google/devtools/cloudtrace_v2/types/__init__.py deleted file mode 100644 index 27530d1f..00000000 --- a/google/devtools/cloudtrace_v2/types/__init__.py +++ /dev/null @@ -1,34 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright 2020 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. -# - -from .trace import ( - AttributeValue, - Module, - Span, - StackTrace, - TruncatableString, -) -from .tracing import BatchWriteSpansRequest - -__all__ = ( - "AttributeValue", - "Module", - "Span", - "StackTrace", - "TruncatableString", - "BatchWriteSpansRequest", -) diff --git a/google/devtools/cloudtrace_v2/types/trace.py b/google/devtools/cloudtrace_v2/types/trace.py deleted file mode 100644 index b877dbf8..00000000 --- a/google/devtools/cloudtrace_v2/types/trace.py +++ /dev/null @@ -1,513 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright 2020 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. -# - -import proto # type: ignore - - -from google.protobuf import timestamp_pb2 as timestamp # type: ignore -from google.protobuf import wrappers_pb2 as wrappers # type: ignore -from google.rpc import status_pb2 as gr_status # type: ignore - - -__protobuf__ = proto.module( - package="google.devtools.cloudtrace.v2", - manifest={"Span", "AttributeValue", "StackTrace", "Module", "TruncatableString",}, -) - - -class Span(proto.Message): - r"""A span represents a single operation within a trace. Spans - can be nested to form a trace tree. Often, a trace contains a - root span that describes the end-to-end latency, and one or more - subspans for its sub-operations. A trace can also contain - multiple root spans, or none at all. Spans do not need to be - contiguous—there may be gaps or overlaps between spans in - a trace. - - Attributes: - name (str): - Required. The resource name of the span in the following - format: - - :: - - projects/[PROJECT_ID]/traces/[TRACE_ID]/spans/[SPAN_ID] - - [TRACE_ID] is a unique identifier for a trace within a - project; it is a 32-character hexadecimal encoding of a - 16-byte array. - - [SPAN_ID] is a unique identifier for a span within a trace; - it is a 16-character hexadecimal encoding of an 8-byte - array. - span_id (str): - Required. The [SPAN_ID] portion of the span's resource name. - parent_span_id (str): - The [SPAN_ID] of this span's parent span. If this is a root - span, then this field must be empty. - display_name (google.devtools.cloudtrace_v2.types.TruncatableString): - Required. A description of the span's - operation (up to 128 bytes). Stackdriver Trace - displays the description in the Google Cloud - Platform Console. - For example, the display name can be a qualified - method name or a file name and a line number - where the operation is called. A best practice - is to use the same display name within an - application and at the same call point. This - makes it easier to correlate spans in different - traces. - start_time (google.protobuf.timestamp_pb2.Timestamp): - Required. The start time of the span. On the - client side, this is the time kept by the local - machine where the span execution starts. On the - server side, this is the time when the server's - application handler starts running. - end_time (google.protobuf.timestamp_pb2.Timestamp): - Required. The end time of the span. On the - client side, this is the time kept by the local - machine where the span execution ends. On the - server side, this is the time when the server - application handler stops running. - attributes (google.devtools.cloudtrace_v2.types.Span.Attributes): - A set of attributes on the span. You can have - up to 32 attributes per span. - stack_trace (google.devtools.cloudtrace_v2.types.StackTrace): - Stack trace captured at the start of the - span. - time_events (google.devtools.cloudtrace_v2.types.Span.TimeEvents): - A set of time events. You can have up to 32 - annotations and 128 message events per span. - links (google.devtools.cloudtrace_v2.types.Span.Links): - Links associated with the span. You can have - up to 128 links per Span. - status (google.rpc.status_pb2.Status): - Optional. The final status for this span. - same_process_as_parent_span (google.protobuf.wrappers_pb2.BoolValue): - Optional. Set this parameter to indicate - whether this span is in the same process as its - parent. If you do not set this parameter, - Stackdriver Trace is unable to take advantage of - this helpful information. - child_span_count (google.protobuf.wrappers_pb2.Int32Value): - Optional. The number of child spans that were - generated while this span was active. If set, - allows implementation to detect missing child - spans. - span_kind (google.devtools.cloudtrace_v2.types.Span.SpanKind): - Optional. Distinguishes between spans generated in a - particular context. For example, two spans with the same - name may be distinguished using ``CLIENT`` (caller) and - ``SERVER`` (callee) to identify an RPC call. - """ - - class SpanKind(proto.Enum): - r"""Type of span. Can be used to specify additional relationships - between spans in addition to a parent/child relationship. - """ - SPAN_KIND_UNSPECIFIED = 0 - INTERNAL = 1 - SERVER = 2 - CLIENT = 3 - PRODUCER = 4 - CONSUMER = 5 - - class Attributes(proto.Message): - r"""A set of attributes, each in the format ``[KEY]:[VALUE]``. - - Attributes: - attribute_map (Sequence[google.devtools.cloudtrace_v2.types.Span.Attributes.AttributeMapEntry]): - The set of attributes. Each attribute's key can be up to 128 - bytes long. The value can be a string up to 256 bytes, a - signed 64-bit integer, or the Boolean values ``true`` and - ``false``. For example: - - :: - - "/instance_id": { "string_value": { "value": "my-instance" } } - "/http/request_bytes": { "int_value": 300 } - "abc.com/myattribute": { "bool_value": false } - dropped_attributes_count (int): - The number of attributes that were discarded. - Attributes can be discarded because their keys - are too long or because there are too many - attributes. If this value is 0 then all - attributes are valid. - """ - - attribute_map = proto.MapField( - proto.STRING, proto.MESSAGE, number=1, message="AttributeValue", - ) - - dropped_attributes_count = proto.Field(proto.INT32, number=2) - - class TimeEvent(proto.Message): - r"""A time-stamped annotation or message event in the Span. - - Attributes: - time (google.protobuf.timestamp_pb2.Timestamp): - The timestamp indicating the time the event - occurred. - annotation (google.devtools.cloudtrace_v2.types.Span.TimeEvent.Annotation): - Text annotation with a set of attributes. - message_event (google.devtools.cloudtrace_v2.types.Span.TimeEvent.MessageEvent): - An event describing a message sent/received - between Spans. - """ - - class Annotation(proto.Message): - r"""Text annotation with a set of attributes. - - Attributes: - description (google.devtools.cloudtrace_v2.types.TruncatableString): - A user-supplied message describing the event. - The maximum length for the description is 256 - bytes. - attributes (google.devtools.cloudtrace_v2.types.Span.Attributes): - A set of attributes on the annotation. You - can have up to 4 attributes per Annotation. - """ - - description = proto.Field( - proto.MESSAGE, number=1, message="TruncatableString", - ) - - attributes = proto.Field( - proto.MESSAGE, number=2, message="Span.Attributes", - ) - - class MessageEvent(proto.Message): - r"""An event describing a message sent/received between Spans. - - Attributes: - type_ (google.devtools.cloudtrace_v2.types.Span.TimeEvent.MessageEvent.Type): - Type of MessageEvent. Indicates whether the - message was sent or received. - id (int): - An identifier for the MessageEvent's message - that can be used to match SENT and RECEIVED - MessageEvents. It is recommended to be unique - within a Span. - uncompressed_size_bytes (int): - The number of uncompressed bytes sent or - received. - compressed_size_bytes (int): - The number of compressed bytes sent or - received. If missing assumed to be the same size - as uncompressed. - """ - - class Type(proto.Enum): - r"""Indicates whether the message was sent or received.""" - TYPE_UNSPECIFIED = 0 - SENT = 1 - RECEIVED = 2 - - type_ = proto.Field( - proto.ENUM, number=1, enum="Span.TimeEvent.MessageEvent.Type", - ) - - id = proto.Field(proto.INT64, number=2) - - uncompressed_size_bytes = proto.Field(proto.INT64, number=3) - - compressed_size_bytes = proto.Field(proto.INT64, number=4) - - time = proto.Field(proto.MESSAGE, number=1, message=timestamp.Timestamp,) - - annotation = proto.Field( - proto.MESSAGE, number=2, oneof="value", message="Span.TimeEvent.Annotation", - ) - - message_event = proto.Field( - proto.MESSAGE, - number=3, - oneof="value", - message="Span.TimeEvent.MessageEvent", - ) - - class TimeEvents(proto.Message): - r"""A collection of ``TimeEvent``\ s. A ``TimeEvent`` is a time-stamped - annotation on the span, consisting of either user-supplied key:value - pairs, or details of a message sent/received between Spans. - - Attributes: - time_event (Sequence[google.devtools.cloudtrace_v2.types.Span.TimeEvent]): - A collection of ``TimeEvent``\ s. - dropped_annotations_count (int): - The number of dropped annotations in all the - included time events. If the value is 0, then no - annotations were dropped. - dropped_message_events_count (int): - The number of dropped message events in all - the included time events. If the value is 0, - then no message events were dropped. - """ - - time_event = proto.RepeatedField( - proto.MESSAGE, number=1, message="Span.TimeEvent", - ) - - dropped_annotations_count = proto.Field(proto.INT32, number=2) - - dropped_message_events_count = proto.Field(proto.INT32, number=3) - - class Link(proto.Message): - r"""A pointer from the current span to another span in the same - trace or in a different trace. For example, this can be used in - batching operations, where a single batch handler processes - multiple requests from different traces or when the handler - receives a request from a different project. - - Attributes: - trace_id (str): - The [TRACE_ID] for a trace within a project. - span_id (str): - The [SPAN_ID] for a span within a trace. - type_ (google.devtools.cloudtrace_v2.types.Span.Link.Type): - The relationship of the current span relative - to the linked span. - attributes (google.devtools.cloudtrace_v2.types.Span.Attributes): - A set of attributes on the link. You have - have up to 32 attributes per link. - """ - - class Type(proto.Enum): - r"""The relationship of the current span relative to the linked - span: child, parent, or unspecified. - """ - TYPE_UNSPECIFIED = 0 - CHILD_LINKED_SPAN = 1 - PARENT_LINKED_SPAN = 2 - - trace_id = proto.Field(proto.STRING, number=1) - - span_id = proto.Field(proto.STRING, number=2) - - type_ = proto.Field(proto.ENUM, number=3, enum="Span.Link.Type",) - - attributes = proto.Field(proto.MESSAGE, number=4, message="Span.Attributes",) - - class Links(proto.Message): - r"""A collection of links, which are references from this span to - a span in the same or different trace. - - Attributes: - link (Sequence[google.devtools.cloudtrace_v2.types.Span.Link]): - A collection of links. - dropped_links_count (int): - The number of dropped links after the maximum - size was enforced. If this value is 0, then no - links were dropped. - """ - - link = proto.RepeatedField(proto.MESSAGE, number=1, message="Span.Link",) - - dropped_links_count = proto.Field(proto.INT32, number=2) - - name = proto.Field(proto.STRING, number=1) - - span_id = proto.Field(proto.STRING, number=2) - - parent_span_id = proto.Field(proto.STRING, number=3) - - display_name = proto.Field(proto.MESSAGE, number=4, message="TruncatableString",) - - start_time = proto.Field(proto.MESSAGE, number=5, message=timestamp.Timestamp,) - - end_time = proto.Field(proto.MESSAGE, number=6, message=timestamp.Timestamp,) - - attributes = proto.Field(proto.MESSAGE, number=7, message=Attributes,) - - stack_trace = proto.Field(proto.MESSAGE, number=8, message="StackTrace",) - - time_events = proto.Field(proto.MESSAGE, number=9, message=TimeEvents,) - - links = proto.Field(proto.MESSAGE, number=10, message=Links,) - - status = proto.Field(proto.MESSAGE, number=11, message=gr_status.Status,) - - same_process_as_parent_span = proto.Field( - proto.MESSAGE, number=12, message=wrappers.BoolValue, - ) - - child_span_count = proto.Field( - proto.MESSAGE, number=13, message=wrappers.Int32Value, - ) - - span_kind = proto.Field(proto.ENUM, number=14, enum=SpanKind,) - - -class AttributeValue(proto.Message): - r"""The allowed types for [VALUE] in a ``[KEY]:[VALUE]`` attribute. - - Attributes: - string_value (google.devtools.cloudtrace_v2.types.TruncatableString): - A string up to 256 bytes long. - int_value (int): - A 64-bit signed integer. - bool_value (bool): - A Boolean value represented by ``true`` or ``false``. - """ - - string_value = proto.Field( - proto.MESSAGE, number=1, oneof="value", message="TruncatableString", - ) - - int_value = proto.Field(proto.INT64, number=2, oneof="value") - - bool_value = proto.Field(proto.BOOL, number=3, oneof="value") - - -class StackTrace(proto.Message): - r"""A call stack appearing in a trace. - - Attributes: - stack_frames (google.devtools.cloudtrace_v2.types.StackTrace.StackFrames): - Stack frames in this stack trace. A maximum - of 128 frames are allowed. - stack_trace_hash_id (int): - The hash ID is used to conserve network bandwidth for - duplicate stack traces within a single trace. - - Often multiple spans will have identical stack traces. The - first occurrence of a stack trace should contain both the - ``stackFrame`` content and a value in ``stackTraceHashId``. - - Subsequent spans within the same request can refer to that - stack trace by only setting ``stackTraceHashId``. - """ - - class StackFrame(proto.Message): - r"""Represents a single stack frame in a stack trace. - - Attributes: - function_name (google.devtools.cloudtrace_v2.types.TruncatableString): - The fully-qualified name that uniquely - identifies the function or method that is active - in this frame (up to 1024 bytes). - original_function_name (google.devtools.cloudtrace_v2.types.TruncatableString): - An un-mangled function name, if ``function_name`` is - `mangled `__. - The name can be fully-qualified (up to 1024 bytes). - file_name (google.devtools.cloudtrace_v2.types.TruncatableString): - The name of the source file where the - function call appears (up to 256 bytes). - line_number (int): - The line number in ``file_name`` where the function call - appears. - column_number (int): - The column number where the function call - appears, if available. This is important in - JavaScript because of its anonymous functions. - load_module (google.devtools.cloudtrace_v2.types.Module): - The binary module from where the code was - loaded. - source_version (google.devtools.cloudtrace_v2.types.TruncatableString): - The version of the deployed source code (up - to 128 bytes). - """ - - function_name = proto.Field( - proto.MESSAGE, number=1, message="TruncatableString", - ) - - original_function_name = proto.Field( - proto.MESSAGE, number=2, message="TruncatableString", - ) - - file_name = proto.Field(proto.MESSAGE, number=3, message="TruncatableString",) - - line_number = proto.Field(proto.INT64, number=4) - - column_number = proto.Field(proto.INT64, number=5) - - load_module = proto.Field(proto.MESSAGE, number=6, message="Module",) - - source_version = proto.Field( - proto.MESSAGE, number=7, message="TruncatableString", - ) - - class StackFrames(proto.Message): - r"""A collection of stack frames, which can be truncated. - - Attributes: - frame (Sequence[google.devtools.cloudtrace_v2.types.StackTrace.StackFrame]): - Stack frames in this call stack. - dropped_frames_count (int): - The number of stack frames that were dropped - because there were too many stack frames. - If this value is 0, then no stack frames were - dropped. - """ - - frame = proto.RepeatedField( - proto.MESSAGE, number=1, message="StackTrace.StackFrame", - ) - - dropped_frames_count = proto.Field(proto.INT32, number=2) - - stack_frames = proto.Field(proto.MESSAGE, number=1, message=StackFrames,) - - stack_trace_hash_id = proto.Field(proto.INT64, number=2) - - -class Module(proto.Message): - r"""Binary module. - - Attributes: - module (google.devtools.cloudtrace_v2.types.TruncatableString): - For example: main binary, kernel modules, and - dynamic libraries such as libc.so, sharedlib.so - (up to 256 bytes). - build_id (google.devtools.cloudtrace_v2.types.TruncatableString): - A unique identifier for the module, usually a - hash of its contents (up to 128 bytes). - """ - - module = proto.Field(proto.MESSAGE, number=1, message="TruncatableString",) - - build_id = proto.Field(proto.MESSAGE, number=2, message="TruncatableString",) - - -class TruncatableString(proto.Message): - r"""Represents a string that might be shortened to a specified - length. - - Attributes: - value (str): - The shortened string. For example, if the original string is - 500 bytes long and the limit of the string is 128 bytes, - then ``value`` contains the first 128 bytes of the 500-byte - string. - - Truncation always happens on a UTF8 character boundary. If - there are multi-byte characters in the string, then the - length of the shortened string might be less than the size - limit. - truncated_byte_count (int): - The number of bytes removed from the original - string. If this value is 0, then the string was - not shortened. - """ - - value = proto.Field(proto.STRING, number=1) - - truncated_byte_count = proto.Field(proto.INT32, number=2) - - -__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/devtools/cloudtrace_v2/types/tracing.py b/google/devtools/cloudtrace_v2/types/tracing.py deleted file mode 100644 index 9083d55b..00000000 --- a/google/devtools/cloudtrace_v2/types/tracing.py +++ /dev/null @@ -1,47 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright 2020 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. -# - -import proto # type: ignore - - -from google.devtools.cloudtrace_v2.types import trace - - -__protobuf__ = proto.module( - package="google.devtools.cloudtrace.v2", manifest={"BatchWriteSpansRequest",}, -) - - -class BatchWriteSpansRequest(proto.Message): - r"""The request message for the ``BatchWriteSpans`` method. - - Attributes: - name (str): - Required. The name of the project where the spans belong. - The format is ``projects/[PROJECT_ID]``. - spans (Sequence[google.devtools.cloudtrace_v2.types.Span]): - Required. A list of new spans. The span names - must not match existing spans, or the results - are undefined. - """ - - name = proto.Field(proto.STRING, number=1) - - spans = proto.RepeatedField(proto.MESSAGE, number=2, message=trace.Span,) - - -__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/noxfile.py b/noxfile.py index 8e9b9413..4d37cd3a 100644 --- a/noxfile.py +++ b/noxfile.py @@ -179,7 +179,7 @@ def cover(session): test runs (not system test runs), and then erases coverage data. """ session.install("coverage", "pytest-cov") - session.run("coverage", "report", "--show-missing", "--fail-under=100") + session.run("coverage", "report", "--show-missing", "--fail-under=98") session.run("coverage", "erase") @@ -211,9 +211,7 @@ def docfx(session): """Build the docfx yaml files for this library.""" session.install("-e", ".") - # sphinx-docfx-yaml supports up to sphinx version 1.5.5. - # https://github.com/docascode/sphinx-docfx-yaml/issues/97 - session.install("sphinx==1.5.5", "alabaster", "recommonmark", "sphinx-docfx-yaml") + session.install("sphinx", "alabaster", "recommonmark", "gcp-sphinx-docfx-yaml") shutil.rmtree(os.path.join("docs", "_build"), ignore_errors=True) session.run( diff --git a/scripts/fixup_cloudtrace_v1_keywords.py b/scripts/fixup_cloudtrace_v1_keywords.py deleted file mode 100644 index 94ec9308..00000000 --- a/scripts/fixup_cloudtrace_v1_keywords.py +++ /dev/null @@ -1,181 +0,0 @@ -#! /usr/bin/env python3 -# -*- coding: utf-8 -*- - -# Copyright 2020 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. -# - -import argparse -import os -import libcst as cst -import pathlib -import sys -from typing import (Any, Callable, Dict, List, Sequence, Tuple) - - -def partition( - predicate: Callable[[Any], bool], - iterator: Sequence[Any] -) -> Tuple[List[Any], List[Any]]: - """A stable, out-of-place partition.""" - results = ([], []) - - for i in iterator: - results[int(predicate(i))].append(i) - - # Returns trueList, falseList - return results[1], results[0] - - -class cloudtraceCallTransformer(cst.CSTTransformer): - CTRL_PARAMS: Tuple[str] = ('retry', 'timeout', 'metadata') - METHOD_TO_PARAMS: Dict[str, Tuple[str]] = { - 'get_trace': ('project_id', 'trace_id', ), - 'list_traces': ('project_id', 'view', 'page_size', 'page_token', 'start_time', 'end_time', 'filter', 'order_by', ), - 'patch_traces': ('project_id', 'traces', ), - - } - - def leave_Call(self, original: cst.Call, updated: cst.Call) -> cst.CSTNode: - try: - key = original.func.attr.value - kword_params = self.METHOD_TO_PARAMS[key] - except (AttributeError, KeyError): - # Either not a method from the API or too convoluted to be sure. - return updated - - # If the existing code is valid, keyword args come after positional args. - # Therefore, all positional args must map to the first parameters. - args, kwargs = partition(lambda a: not bool(a.keyword), updated.args) - if any(k.keyword.value == "request" for k in kwargs): - # We've already fixed this file, don't fix it again. - return updated - - kwargs, ctrl_kwargs = partition( - lambda a: not a.keyword.value in self.CTRL_PARAMS, - kwargs - ) - - args, ctrl_args = args[:len(kword_params)], args[len(kword_params):] - ctrl_kwargs.extend(cst.Arg(value=a.value, keyword=cst.Name(value=ctrl)) - for a, ctrl in zip(ctrl_args, self.CTRL_PARAMS)) - - request_arg = cst.Arg( - value=cst.Dict([ - cst.DictElement( - cst.SimpleString("'{}'".format(name)), - cst.Element(value=arg.value) - ) - # Note: the args + kwargs looks silly, but keep in mind that - # the control parameters had to be stripped out, and that - # those could have been passed positionally or by keyword. - for name, arg in zip(kword_params, args + kwargs)]), - keyword=cst.Name("request") - ) - - return updated.with_changes( - args=[request_arg] + ctrl_kwargs - ) - - -def fix_files( - in_dir: pathlib.Path, - out_dir: pathlib.Path, - *, - transformer=cloudtraceCallTransformer(), -): - """Duplicate the input dir to the output dir, fixing file method calls. - - Preconditions: - * in_dir is a real directory - * out_dir is a real, empty directory - """ - pyfile_gen = ( - pathlib.Path(os.path.join(root, f)) - for root, _, files in os.walk(in_dir) - for f in files if os.path.splitext(f)[1] == ".py" - ) - - for fpath in pyfile_gen: - with open(fpath, 'r') as f: - src = f.read() - - # Parse the code and insert method call fixes. - tree = cst.parse_module(src) - updated = tree.visit(transformer) - - # Create the path and directory structure for the new file. - updated_path = out_dir.joinpath(fpath.relative_to(in_dir)) - updated_path.parent.mkdir(parents=True, exist_ok=True) - - # Generate the updated source file at the corresponding path. - with open(updated_path, 'w') as f: - f.write(updated.code) - - -if __name__ == '__main__': - parser = argparse.ArgumentParser( - description="""Fix up source that uses the cloudtrace client library. - -The existing sources are NOT overwritten but are copied to output_dir with changes made. - -Note: This tool operates at a best-effort level at converting positional - parameters in client method calls to keyword based parameters. - Cases where it WILL FAIL include - A) * or ** expansion in a method call. - B) Calls via function or method alias (includes free function calls) - C) Indirect or dispatched calls (e.g. the method is looked up dynamically) - - These all constitute false negatives. The tool will also detect false - positives when an API method shares a name with another method. -""") - parser.add_argument( - '-d', - '--input-directory', - required=True, - dest='input_dir', - help='the input directory to walk for python files to fix up', - ) - parser.add_argument( - '-o', - '--output-directory', - required=True, - dest='output_dir', - help='the directory to output files fixed via un-flattening', - ) - args = parser.parse_args() - input_dir = pathlib.Path(args.input_dir) - output_dir = pathlib.Path(args.output_dir) - if not input_dir.is_dir(): - print( - f"input directory '{input_dir}' does not exist or is not a directory", - file=sys.stderr, - ) - sys.exit(-1) - - if not output_dir.is_dir(): - print( - f"output directory '{output_dir}' does not exist or is not a directory", - file=sys.stderr, - ) - sys.exit(-1) - - if os.listdir(output_dir): - print( - f"output directory '{output_dir}' is not empty", - file=sys.stderr, - ) - sys.exit(-1) - - fix_files(input_dir, output_dir) diff --git a/scripts/fixup_cloudtrace_v2_keywords.py b/scripts/fixup_cloudtrace_v2_keywords.py deleted file mode 100644 index 08772451..00000000 --- a/scripts/fixup_cloudtrace_v2_keywords.py +++ /dev/null @@ -1,180 +0,0 @@ -#! /usr/bin/env python3 -# -*- coding: utf-8 -*- - -# Copyright 2020 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. -# - -import argparse -import os -import libcst as cst -import pathlib -import sys -from typing import (Any, Callable, Dict, List, Sequence, Tuple) - - -def partition( - predicate: Callable[[Any], bool], - iterator: Sequence[Any] -) -> Tuple[List[Any], List[Any]]: - """A stable, out-of-place partition.""" - results = ([], []) - - for i in iterator: - results[int(predicate(i))].append(i) - - # Returns trueList, falseList - return results[1], results[0] - - -class cloudtraceCallTransformer(cst.CSTTransformer): - CTRL_PARAMS: Tuple[str] = ('retry', 'timeout', 'metadata') - METHOD_TO_PARAMS: Dict[str, Tuple[str]] = { - 'batch_write_spans': ('name', 'spans', ), - 'create_span': ('name', 'span_id', 'display_name', 'start_time', 'end_time', 'parent_span_id', 'attributes', 'stack_trace', 'time_events', 'links', 'status', 'same_process_as_parent_span', 'child_span_count', 'span_kind', ), - - } - - def leave_Call(self, original: cst.Call, updated: cst.Call) -> cst.CSTNode: - try: - key = original.func.attr.value - kword_params = self.METHOD_TO_PARAMS[key] - except (AttributeError, KeyError): - # Either not a method from the API or too convoluted to be sure. - return updated - - # If the existing code is valid, keyword args come after positional args. - # Therefore, all positional args must map to the first parameters. - args, kwargs = partition(lambda a: not bool(a.keyword), updated.args) - if any(k.keyword.value == "request" for k in kwargs): - # We've already fixed this file, don't fix it again. - return updated - - kwargs, ctrl_kwargs = partition( - lambda a: not a.keyword.value in self.CTRL_PARAMS, - kwargs - ) - - args, ctrl_args = args[:len(kword_params)], args[len(kword_params):] - ctrl_kwargs.extend(cst.Arg(value=a.value, keyword=cst.Name(value=ctrl)) - for a, ctrl in zip(ctrl_args, self.CTRL_PARAMS)) - - request_arg = cst.Arg( - value=cst.Dict([ - cst.DictElement( - cst.SimpleString("'{}'".format(name)), - cst.Element(value=arg.value) - ) - # Note: the args + kwargs looks silly, but keep in mind that - # the control parameters had to be stripped out, and that - # those could have been passed positionally or by keyword. - for name, arg in zip(kword_params, args + kwargs)]), - keyword=cst.Name("request") - ) - - return updated.with_changes( - args=[request_arg] + ctrl_kwargs - ) - - -def fix_files( - in_dir: pathlib.Path, - out_dir: pathlib.Path, - *, - transformer=cloudtraceCallTransformer(), -): - """Duplicate the input dir to the output dir, fixing file method calls. - - Preconditions: - * in_dir is a real directory - * out_dir is a real, empty directory - """ - pyfile_gen = ( - pathlib.Path(os.path.join(root, f)) - for root, _, files in os.walk(in_dir) - for f in files if os.path.splitext(f)[1] == ".py" - ) - - for fpath in pyfile_gen: - with open(fpath, 'r') as f: - src = f.read() - - # Parse the code and insert method call fixes. - tree = cst.parse_module(src) - updated = tree.visit(transformer) - - # Create the path and directory structure for the new file. - updated_path = out_dir.joinpath(fpath.relative_to(in_dir)) - updated_path.parent.mkdir(parents=True, exist_ok=True) - - # Generate the updated source file at the corresponding path. - with open(updated_path, 'w') as f: - f.write(updated.code) - - -if __name__ == '__main__': - parser = argparse.ArgumentParser( - description="""Fix up source that uses the cloudtrace client library. - -The existing sources are NOT overwritten but are copied to output_dir with changes made. - -Note: This tool operates at a best-effort level at converting positional - parameters in client method calls to keyword based parameters. - Cases where it WILL FAIL include - A) * or ** expansion in a method call. - B) Calls via function or method alias (includes free function calls) - C) Indirect or dispatched calls (e.g. the method is looked up dynamically) - - These all constitute false negatives. The tool will also detect false - positives when an API method shares a name with another method. -""") - parser.add_argument( - '-d', - '--input-directory', - required=True, - dest='input_dir', - help='the input directory to walk for python files to fix up', - ) - parser.add_argument( - '-o', - '--output-directory', - required=True, - dest='output_dir', - help='the directory to output files fixed via un-flattening', - ) - args = parser.parse_args() - input_dir = pathlib.Path(args.input_dir) - output_dir = pathlib.Path(args.output_dir) - if not input_dir.is_dir(): - print( - f"input directory '{input_dir}' does not exist or is not a directory", - file=sys.stderr, - ) - sys.exit(-1) - - if not output_dir.is_dir(): - print( - f"output directory '{output_dir}' does not exist or is not a directory", - file=sys.stderr, - ) - sys.exit(-1) - - if os.listdir(output_dir): - print( - f"output directory '{output_dir}' is not empty", - file=sys.stderr, - ) - sys.exit(-1) - - fix_files(input_dir, output_dir) diff --git a/scripts/fixup_trace_v1_keywords.py b/scripts/fixup_trace_v1_keywords.py index 71a02d87..29536f6a 100644 --- a/scripts/fixup_trace_v1_keywords.py +++ b/scripts/fixup_trace_v1_keywords.py @@ -1,3 +1,4 @@ +#! /usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright 2020 Google LLC diff --git a/scripts/fixup_trace_v2_keywords.py b/scripts/fixup_trace_v2_keywords.py index f01c1004..ec15f7f3 100644 --- a/scripts/fixup_trace_v2_keywords.py +++ b/scripts/fixup_trace_v2_keywords.py @@ -1,3 +1,4 @@ +#! /usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright 2020 Google LLC diff --git a/setup.py b/setup.py index f0a8058f..5336b75b 100644 --- a/setup.py +++ b/setup.py @@ -29,7 +29,7 @@ # 'Development Status :: 5 - Production/Stable' release_status = "Development Status :: 5 - Production/Stable" dependencies = [ - "google-api-core[grpc] >= 1.22.0, < 2.0.0dev", + "google-api-core[grpc] >= 1.22.2, < 2.0.0dev", "proto-plus >= 1.4.0", ] extras = {} diff --git a/synth.metadata b/synth.metadata index e05c458a..549a7fa5 100644 --- a/synth.metadata +++ b/synth.metadata @@ -3,30 +3,22 @@ { "git": { "name": ".", - "remote": "https://github.com/googleapis/python-trace.git", - "sha": "2f11aa911b62c439a0a77926c3a6ffd940f065c5" - } - }, - { - "git": { - "name": "googleapis", - "remote": "https://github.com/googleapis/googleapis.git", - "sha": "a1af63efb82f54428ab35ea76869d9cd57ca52b8", - "internalRef": "364635275" + "remote": "git@github.com:googleapis/python-trace", + "sha": "8685deeca16bd59a8f3baadd8bbc964ddb0248c8" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "86ed43d4f56e6404d068e62e497029018879c771" + "sha": "ff39353f34a36e7643b86e97724e4027ab466dc6" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "86ed43d4f56e6404d068e62e497029018879c771" + "sha": "ff39353f34a36e7643b86e97724e4027ab466dc6" } } ], @@ -49,126 +41,5 @@ "generator": "bazel" } } - ], - "generatedFiles": [ - ".coveragerc", - ".flake8", - ".github/CONTRIBUTING.md", - ".github/ISSUE_TEMPLATE/bug_report.md", - ".github/ISSUE_TEMPLATE/feature_request.md", - ".github/ISSUE_TEMPLATE/support_request.md", - ".github/PULL_REQUEST_TEMPLATE.md", - ".github/header-checker-lint.yml", - ".github/release-please.yml", - ".github/snippet-bot.yml", - ".gitignore", - ".kokoro/build.sh", - ".kokoro/continuous/common.cfg", - ".kokoro/continuous/continuous.cfg", - ".kokoro/docker/docs/Dockerfile", - ".kokoro/docker/docs/fetch_gpg_keys.sh", - ".kokoro/docs/common.cfg", - ".kokoro/docs/docs-presubmit.cfg", - ".kokoro/docs/docs.cfg", - ".kokoro/populate-secrets.sh", - ".kokoro/presubmit/common.cfg", - ".kokoro/presubmit/presubmit.cfg", - ".kokoro/publish-docs.sh", - ".kokoro/release.sh", - ".kokoro/release/common.cfg", - ".kokoro/release/release.cfg", - ".kokoro/samples/lint/common.cfg", - ".kokoro/samples/lint/continuous.cfg", - ".kokoro/samples/lint/periodic.cfg", - ".kokoro/samples/lint/presubmit.cfg", - ".kokoro/samples/python3.6/common.cfg", - ".kokoro/samples/python3.6/continuous.cfg", - ".kokoro/samples/python3.6/periodic-head.cfg", - ".kokoro/samples/python3.6/periodic.cfg", - ".kokoro/samples/python3.6/presubmit.cfg", - ".kokoro/samples/python3.7/common.cfg", - ".kokoro/samples/python3.7/continuous.cfg", - ".kokoro/samples/python3.7/periodic-head.cfg", - ".kokoro/samples/python3.7/periodic.cfg", - ".kokoro/samples/python3.7/presubmit.cfg", - ".kokoro/samples/python3.8/common.cfg", - ".kokoro/samples/python3.8/continuous.cfg", - ".kokoro/samples/python3.8/periodic-head.cfg", - ".kokoro/samples/python3.8/periodic.cfg", - ".kokoro/samples/python3.8/presubmit.cfg", - ".kokoro/test-samples-against-head.sh", - ".kokoro/test-samples-impl.sh", - ".kokoro/test-samples.sh", - ".kokoro/trampoline.sh", - ".kokoro/trampoline_v2.sh", - ".pre-commit-config.yaml", - ".trampolinerc", - "CODE_OF_CONDUCT.md", - "CONTRIBUTING.rst", - "LICENSE", - "MANIFEST.in", - "README.rst", - "docs/_static/custom.css", - "docs/_templates/layout.html", - "docs/cloudtrace_v1/services.rst", - "docs/cloudtrace_v1/trace_service.rst", - "docs/cloudtrace_v1/types.rst", - "docs/cloudtrace_v2/services.rst", - "docs/cloudtrace_v2/trace_service.rst", - "docs/cloudtrace_v2/types.rst", - "docs/conf.py", - "docs/multiprocessing.rst", - "google/cloud/trace_v1/proto/trace.proto", - "google/cloud/trace_v2/proto/trace.proto", - "google/cloud/trace_v2/proto/tracing.proto", - "google/devtools/cloudtrace/__init__.py", - "google/devtools/cloudtrace/py.typed", - "google/devtools/cloudtrace_v1/__init__.py", - "google/devtools/cloudtrace_v1/py.typed", - "google/devtools/cloudtrace_v1/services/__init__.py", - "google/devtools/cloudtrace_v1/services/trace_service/__init__.py", - "google/devtools/cloudtrace_v1/services/trace_service/async_client.py", - "google/devtools/cloudtrace_v1/services/trace_service/client.py", - "google/devtools/cloudtrace_v1/services/trace_service/pagers.py", - "google/devtools/cloudtrace_v1/services/trace_service/transports/__init__.py", - "google/devtools/cloudtrace_v1/services/trace_service/transports/base.py", - "google/devtools/cloudtrace_v1/services/trace_service/transports/grpc.py", - "google/devtools/cloudtrace_v1/services/trace_service/transports/grpc_asyncio.py", - "google/devtools/cloudtrace_v1/types/__init__.py", - "google/devtools/cloudtrace_v1/types/trace.py", - "google/devtools/cloudtrace_v2/__init__.py", - "google/devtools/cloudtrace_v2/py.typed", - "google/devtools/cloudtrace_v2/services/__init__.py", - "google/devtools/cloudtrace_v2/services/trace_service/__init__.py", - "google/devtools/cloudtrace_v2/services/trace_service/async_client.py", - "google/devtools/cloudtrace_v2/services/trace_service/client.py", - "google/devtools/cloudtrace_v2/services/trace_service/transports/__init__.py", - "google/devtools/cloudtrace_v2/services/trace_service/transports/base.py", - "google/devtools/cloudtrace_v2/services/trace_service/transports/grpc.py", - "google/devtools/cloudtrace_v2/services/trace_service/transports/grpc_asyncio.py", - "google/devtools/cloudtrace_v2/types/__init__.py", - "google/devtools/cloudtrace_v2/types/trace.py", - "google/devtools/cloudtrace_v2/types/tracing.py", - "mypy.ini", - "noxfile.py", - "renovate.json", - "samples/AUTHORING_GUIDE.md", - "samples/CONTRIBUTING.md", - "samples/snippets/noxfile.py", - "scripts/decrypt-secrets.sh", - "scripts/fixup_cloudtrace_v1_keywords.py", - "scripts/fixup_cloudtrace_v2_keywords.py", - "scripts/readme-gen/readme_gen.py", - "scripts/readme-gen/templates/README.tmpl.rst", - "scripts/readme-gen/templates/auth.tmpl.rst", - "scripts/readme-gen/templates/auth_api_key.tmpl.rst", - "scripts/readme-gen/templates/install_deps.tmpl.rst", - "scripts/readme-gen/templates/install_portaudio.tmpl.rst", - "setup.cfg", - "testing/.gitignore", - "tests/unit/gapic/cloudtrace_v1/__init__.py", - "tests/unit/gapic/cloudtrace_v1/test_trace_service.py", - "tests/unit/gapic/cloudtrace_v2/__init__.py", - "tests/unit/gapic/cloudtrace_v2/test_trace_service.py" ] } \ No newline at end of file diff --git a/synth.py b/synth.py index 982a2bb7..4d00182e 100644 --- a/synth.py +++ b/synth.py @@ -42,6 +42,7 @@ templated_files = common.py_library( samples=True, # set to True only if there are samples microgenerator=True, + cov_level=98, ) python.py_samples(skip_readmes=True) diff --git a/testing/constraints-3.6.txt b/testing/constraints-3.6.txt index 8bee7da4..a37a34af 100644 --- a/testing/constraints-3.6.txt +++ b/testing/constraints-3.6.txt @@ -5,5 +5,5 @@ # # e.g., if setup.py has "foo >= 1.14.0, < 2.0.0dev", # Then this file should have foo==1.14.0 -google-api-core==1.22.0 -proto-plus==1.4.0 \ No newline at end of file +google-api-core==1.22.2 +proto-plus==1.4.0 diff --git a/tests/system/gapic/v1/test_system_trace_service_v1.py b/tests/system/gapic/v1/test_system_trace_service_v1.py deleted file mode 100644 index d6bc7a78..00000000 --- a/tests/system/gapic/v1/test_system_trace_service_v1.py +++ /dev/null @@ -1,27 +0,0 @@ -# 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 -# -# https://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. - -import os -import time - -from google.cloud import trace_v1 - - -class TestSystemTraceService(object): - def test_list_traces(self): - project_id = os.environ["PROJECT_ID"] - - client = trace_v1.TraceServiceClient() - project_id_2 = project_id - response = client.list_traces(project_id=project_id_2) diff --git a/tests/system/gapic/v1/test_system_trace_service_v1_vpcsc.py b/tests/system/gapic/v1/test_system_trace_service_v1_vpcsc.py deleted file mode 100644 index 4fd73811..00000000 --- a/tests/system/gapic/v1/test_system_trace_service_v1_vpcsc.py +++ /dev/null @@ -1,76 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright 2019 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 -# -# https://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. - -# flake8: noqa - -import os -import pytest - -from google.api_core import exceptions -from google.cloud import trace_v1 -from test_utils.vpcsc_config import vpcsc_config - -_VPCSC_PROHIBITED_MESSAGE = "Request is prohibited by organization's policy." - - -@pytest.fixture -def client(): - return trace_v1.TraceServiceClient() - - -@vpcsc_config.skip_unless_inside_vpcsc -def test_list_traces_w_inside(client): - list(client.list_traces(project_id=vpcsc_config.project_inside)) # no perms issue - - -@vpcsc_config.skip_unless_inside_vpcsc -def test_list_traces_w_outside(client): - with pytest.raises(exceptions.PermissionDenied) as exc: - list(client.list_traces(project_id=vpcsc_config.project_outside)) - - assert _VPCSC_PROHIBITED_MESSAGE in exc.value.message - - -@vpcsc_config.skip_unless_inside_vpcsc -def test_get_trace_w_inside(client): - with pytest.raises(exceptions.InvalidArgument): - client.get_trace( - project_id=vpcsc_config.project_inside, trace_id="" - ) # no perms issue - - -@vpcsc_config.skip_unless_inside_vpcsc -def test_get_trace_w_outside(client): - with pytest.raises(exceptions.PermissionDenied) as exc: - client.get_trace(project_id=vpcsc_config.project_outside, trace_id="") - - assert _VPCSC_PROHIBITED_MESSAGE in exc.value.message - - -@vpcsc_config.skip_unless_inside_vpcsc -def test_patch_traces_w_inside(client): - with pytest.raises(exceptions.InvalidArgument): - client.patch_traces( - project_id=vpcsc_config.project_inside, traces={} - ) # no perms issue - - -@vpcsc_config.skip_unless_inside_vpcsc -def test_patch_traces_w_ouside(client): - with pytest.raises(exceptions.PermissionDenied) as exc: - client.patch_traces(project_id=vpcsc_config.project_outside, traces={}) - - assert _VPCSC_PROHIBITED_MESSAGE in exc.value.message diff --git a/tests/system/gapic/v2/test_system_trace_service_v2.py b/tests/system/gapic/v2/test_system_trace_service_v2.py deleted file mode 100644 index 0a73506a..00000000 --- a/tests/system/gapic/v2/test_system_trace_service_v2.py +++ /dev/null @@ -1,28 +0,0 @@ -# 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 -# -# https://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. - -import os -import time - -from google.cloud import trace_v2 - - -class TestSystemTraceService(object): - def test_batch_write_spans(self): - project_id = os.environ["PROJECT_ID"] - - client = trace_v2.TraceServiceClient() - name = f"projects/{project_id}" - spans = [] - client.batch_write_spans(name=name, spans=spans) diff --git a/tests/system/gapic/v2/test_system_trace_service_v2_vpcsc.py b/tests/system/gapic/v2/test_system_trace_service_v2_vpcsc.py deleted file mode 100644 index a373ba51..00000000 --- a/tests/system/gapic/v2/test_system_trace_service_v2_vpcsc.py +++ /dev/null @@ -1,47 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright 2019 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 -# -# https://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. - -# flake8: noqa - -import os -import pytest - -from google.api_core import exceptions -from google.cloud import trace_v2 -from test_utils.vpcsc_config import vpcsc_config - -_VPCSC_PROHIBITED_MESSAGE = "Request is prohibited by organization's policy." - - -@pytest.fixture -def client(): - return trace_v2.TraceServiceClient() - - -@vpcsc_config.skip_unless_inside_vpcsc -def test_batch_write_spans_w_inside(client): - project_inside = f"projects/{vpcsc_config.project_inside}" - client.batch_write_spans(name=project_inside, spans=[]) # no raise - - -@vpcsc_config.skip_unless_inside_vpcsc -def test_batch_write_spans_w_outside(client): - project_outside = f"projects/{vpcsc_config.project_outside}" - - with pytest.raises(exceptions.PermissionDenied) as exc: - client.batch_write_spans(name=project_outside, spans=[]) - - assert _VPCSC_PROHIBITED_MESSAGE in exc.value.message diff --git a/tests/unit/gapic/cloudtrace_v1/__init__.py b/tests/unit/gapic/cloudtrace_v1/__init__.py deleted file mode 100644 index 42ffdf2b..00000000 --- a/tests/unit/gapic/cloudtrace_v1/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright 2020 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. -# diff --git a/tests/unit/gapic/cloudtrace_v1/test_trace_service.py b/tests/unit/gapic/cloudtrace_v1/test_trace_service.py deleted file mode 100644 index 1cd92988..00000000 --- a/tests/unit/gapic/cloudtrace_v1/test_trace_service.py +++ /dev/null @@ -1,1507 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright 2020 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. -# - -import os -import mock - -import grpc -from grpc.experimental import aio -import math -import pytest -from proto.marshal.rules.dates import DurationRule, TimestampRule - -from google import auth -from google.api_core import client_options -from google.api_core import exceptions -from google.api_core import gapic_v1 -from google.api_core import grpc_helpers -from google.api_core import grpc_helpers_async -from google.auth import credentials -from google.auth.exceptions import MutualTLSChannelError -from google.devtools.cloudtrace_v1.services.trace_service import TraceServiceAsyncClient -from google.devtools.cloudtrace_v1.services.trace_service import TraceServiceClient -from google.devtools.cloudtrace_v1.services.trace_service import pagers -from google.devtools.cloudtrace_v1.services.trace_service import transports -from google.devtools.cloudtrace_v1.types import trace -from google.oauth2 import service_account -from google.protobuf import timestamp_pb2 as timestamp # type: ignore - - -def client_cert_source_callback(): - return b"cert bytes", b"key bytes" - - -# If default endpoint is localhost, then default mtls endpoint will be the same. -# This method modifies the default endpoint so the client can produce a different -# mtls endpoint for endpoint testing purposes. -def modify_default_endpoint(client): - return ( - "foo.googleapis.com" - if ("localhost" in client.DEFAULT_ENDPOINT) - else client.DEFAULT_ENDPOINT - ) - - -def test__get_default_mtls_endpoint(): - api_endpoint = "example.googleapis.com" - api_mtls_endpoint = "example.mtls.googleapis.com" - sandbox_endpoint = "example.sandbox.googleapis.com" - sandbox_mtls_endpoint = "example.mtls.sandbox.googleapis.com" - non_googleapi = "api.example.com" - - assert TraceServiceClient._get_default_mtls_endpoint(None) is None - assert ( - TraceServiceClient._get_default_mtls_endpoint(api_endpoint) == api_mtls_endpoint - ) - assert ( - TraceServiceClient._get_default_mtls_endpoint(api_mtls_endpoint) - == api_mtls_endpoint - ) - assert ( - TraceServiceClient._get_default_mtls_endpoint(sandbox_endpoint) - == sandbox_mtls_endpoint - ) - assert ( - TraceServiceClient._get_default_mtls_endpoint(sandbox_mtls_endpoint) - == sandbox_mtls_endpoint - ) - assert TraceServiceClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi - - -@pytest.mark.parametrize("client_class", [TraceServiceClient, TraceServiceAsyncClient,]) -def test_trace_service_client_from_service_account_info(client_class): - creds = credentials.AnonymousCredentials() - with mock.patch.object( - service_account.Credentials, "from_service_account_info" - ) as factory: - factory.return_value = creds - info = {"valid": True} - client = client_class.from_service_account_info(info) - assert client.transport._credentials == creds - assert isinstance(client, client_class) - - assert client.transport._host == "cloudtrace.googleapis.com:443" - - -@pytest.mark.parametrize("client_class", [TraceServiceClient, TraceServiceAsyncClient,]) -def test_trace_service_client_from_service_account_file(client_class): - creds = credentials.AnonymousCredentials() - with mock.patch.object( - service_account.Credentials, "from_service_account_file" - ) as factory: - factory.return_value = creds - client = client_class.from_service_account_file("dummy/file/path.json") - assert client.transport._credentials == creds - assert isinstance(client, client_class) - - client = client_class.from_service_account_json("dummy/file/path.json") - assert client.transport._credentials == creds - assert isinstance(client, client_class) - - assert client.transport._host == "cloudtrace.googleapis.com:443" - - -def test_trace_service_client_get_transport_class(): - transport = TraceServiceClient.get_transport_class() - available_transports = [ - transports.TraceServiceGrpcTransport, - ] - assert transport in available_transports - - transport = TraceServiceClient.get_transport_class("grpc") - assert transport == transports.TraceServiceGrpcTransport - - -@pytest.mark.parametrize( - "client_class,transport_class,transport_name", - [ - (TraceServiceClient, transports.TraceServiceGrpcTransport, "grpc"), - ( - TraceServiceAsyncClient, - transports.TraceServiceGrpcAsyncIOTransport, - "grpc_asyncio", - ), - ], -) -@mock.patch.object( - TraceServiceClient, "DEFAULT_ENDPOINT", modify_default_endpoint(TraceServiceClient) -) -@mock.patch.object( - TraceServiceAsyncClient, - "DEFAULT_ENDPOINT", - modify_default_endpoint(TraceServiceAsyncClient), -) -def test_trace_service_client_client_options( - client_class, transport_class, transport_name -): - # Check that if channel is provided we won't create a new one. - with mock.patch.object(TraceServiceClient, "get_transport_class") as gtc: - transport = transport_class(credentials=credentials.AnonymousCredentials()) - client = client_class(transport=transport) - gtc.assert_not_called() - - # Check that if channel is provided via str we will create a new one. - with mock.patch.object(TraceServiceClient, "get_transport_class") as gtc: - client = client_class(transport=transport_name) - gtc.assert_called() - - # Check the case api_endpoint is provided. - options = client_options.ClientOptions(api_endpoint="squid.clam.whelk") - with mock.patch.object(transport_class, "__init__") as patched: - patched.return_value = None - client = client_class(client_options=options) - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host="squid.clam.whelk", - scopes=None, - client_cert_source_for_mtls=None, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) - - # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is - # "never". - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}): - with mock.patch.object(transport_class, "__init__") as patched: - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=client.DEFAULT_ENDPOINT, - scopes=None, - client_cert_source_for_mtls=None, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) - - # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is - # "always". - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}): - with mock.patch.object(transport_class, "__init__") as patched: - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=client.DEFAULT_MTLS_ENDPOINT, - scopes=None, - client_cert_source_for_mtls=None, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) - - # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT has - # unsupported value. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}): - with pytest.raises(MutualTLSChannelError): - client = client_class() - - # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value. - with mock.patch.dict( - os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} - ): - with pytest.raises(ValueError): - client = client_class() - - # Check the case quota_project_id is provided - options = client_options.ClientOptions(quota_project_id="octopus") - with mock.patch.object(transport_class, "__init__") as patched: - patched.return_value = None - client = client_class(client_options=options) - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=client.DEFAULT_ENDPOINT, - scopes=None, - client_cert_source_for_mtls=None, - quota_project_id="octopus", - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) - - -@pytest.mark.parametrize( - "client_class,transport_class,transport_name,use_client_cert_env", - [ - (TraceServiceClient, transports.TraceServiceGrpcTransport, "grpc", "true"), - ( - TraceServiceAsyncClient, - transports.TraceServiceGrpcAsyncIOTransport, - "grpc_asyncio", - "true", - ), - (TraceServiceClient, transports.TraceServiceGrpcTransport, "grpc", "false"), - ( - TraceServiceAsyncClient, - transports.TraceServiceGrpcAsyncIOTransport, - "grpc_asyncio", - "false", - ), - ], -) -@mock.patch.object( - TraceServiceClient, "DEFAULT_ENDPOINT", modify_default_endpoint(TraceServiceClient) -) -@mock.patch.object( - TraceServiceAsyncClient, - "DEFAULT_ENDPOINT", - modify_default_endpoint(TraceServiceAsyncClient), -) -@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) -def test_trace_service_client_mtls_env_auto( - client_class, transport_class, transport_name, use_client_cert_env -): - # This tests the endpoint autoswitch behavior. Endpoint is autoswitched to the default - # mtls endpoint, if GOOGLE_API_USE_CLIENT_CERTIFICATE is "true" and client cert exists. - - # Check the case client_cert_source is provided. Whether client cert is used depends on - # GOOGLE_API_USE_CLIENT_CERTIFICATE value. - with mock.patch.dict( - os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} - ): - options = client_options.ClientOptions( - client_cert_source=client_cert_source_callback - ) - with mock.patch.object(transport_class, "__init__") as patched: - patched.return_value = None - client = client_class(client_options=options) - - if use_client_cert_env == "false": - expected_client_cert_source = None - expected_host = client.DEFAULT_ENDPOINT - else: - expected_client_cert_source = client_cert_source_callback - expected_host = client.DEFAULT_MTLS_ENDPOINT - - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - client_cert_source_for_mtls=expected_client_cert_source, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) - - # Check the case ADC client cert is provided. Whether client cert is used depends on - # GOOGLE_API_USE_CLIENT_CERTIFICATE value. - with mock.patch.dict( - os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} - ): - with mock.patch.object(transport_class, "__init__") as patched: - with mock.patch( - "google.auth.transport.mtls.has_default_client_cert_source", - return_value=True, - ): - with mock.patch( - "google.auth.transport.mtls.default_client_cert_source", - return_value=client_cert_source_callback, - ): - if use_client_cert_env == "false": - expected_host = client.DEFAULT_ENDPOINT - expected_client_cert_source = None - else: - expected_host = client.DEFAULT_MTLS_ENDPOINT - expected_client_cert_source = client_cert_source_callback - - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - client_cert_source_for_mtls=expected_client_cert_source, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) - - # Check the case client_cert_source and ADC client cert are not provided. - with mock.patch.dict( - os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} - ): - with mock.patch.object(transport_class, "__init__") as patched: - with mock.patch( - "google.auth.transport.mtls.has_default_client_cert_source", - return_value=False, - ): - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=client.DEFAULT_ENDPOINT, - scopes=None, - client_cert_source_for_mtls=None, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) - - -@pytest.mark.parametrize( - "client_class,transport_class,transport_name", - [ - (TraceServiceClient, transports.TraceServiceGrpcTransport, "grpc"), - ( - TraceServiceAsyncClient, - transports.TraceServiceGrpcAsyncIOTransport, - "grpc_asyncio", - ), - ], -) -def test_trace_service_client_client_options_scopes( - client_class, transport_class, transport_name -): - # Check the case scopes are provided. - options = client_options.ClientOptions(scopes=["1", "2"],) - with mock.patch.object(transport_class, "__init__") as patched: - patched.return_value = None - client = client_class(client_options=options) - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=client.DEFAULT_ENDPOINT, - scopes=["1", "2"], - client_cert_source_for_mtls=None, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) - - -@pytest.mark.parametrize( - "client_class,transport_class,transport_name", - [ - (TraceServiceClient, transports.TraceServiceGrpcTransport, "grpc"), - ( - TraceServiceAsyncClient, - transports.TraceServiceGrpcAsyncIOTransport, - "grpc_asyncio", - ), - ], -) -def test_trace_service_client_client_options_credentials_file( - client_class, transport_class, transport_name -): - # Check the case credentials file is provided. - options = client_options.ClientOptions(credentials_file="credentials.json") - with mock.patch.object(transport_class, "__init__") as patched: - patched.return_value = None - client = client_class(client_options=options) - patched.assert_called_once_with( - credentials=None, - credentials_file="credentials.json", - host=client.DEFAULT_ENDPOINT, - scopes=None, - client_cert_source_for_mtls=None, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) - - -def test_trace_service_client_client_options_from_dict(): - with mock.patch( - "google.devtools.cloudtrace_v1.services.trace_service.transports.TraceServiceGrpcTransport.__init__" - ) as grpc_transport: - grpc_transport.return_value = None - client = TraceServiceClient(client_options={"api_endpoint": "squid.clam.whelk"}) - grpc_transport.assert_called_once_with( - credentials=None, - credentials_file=None, - host="squid.clam.whelk", - scopes=None, - client_cert_source_for_mtls=None, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) - - -def test_list_traces(transport: str = "grpc", request_type=trace.ListTracesRequest): - client = TraceServiceClient( - credentials=credentials.AnonymousCredentials(), transport=transport, - ) - - # Everything is optional in proto3 as far as the runtime is concerned, - # and we are mocking out the actual API, so just send an empty request. - request = request_type() - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.list_traces), "__call__") as call: - # Designate an appropriate return value for the call. - call.return_value = trace.ListTracesResponse( - next_page_token="next_page_token_value", - ) - - response = client.list_traces(request) - - # Establish that the underlying gRPC stub method was called. - assert len(call.mock_calls) == 1 - _, args, _ = call.mock_calls[0] - - assert args[0] == trace.ListTracesRequest() - - # Establish that the response is the type that we expect. - - assert isinstance(response, pagers.ListTracesPager) - - assert response.next_page_token == "next_page_token_value" - - -def test_list_traces_from_dict(): - test_list_traces(request_type=dict) - - -def test_list_traces_empty_call(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = TraceServiceClient( - credentials=credentials.AnonymousCredentials(), transport="grpc", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.list_traces), "__call__") as call: - client.list_traces() - call.assert_called() - _, args, _ = call.mock_calls[0] - - assert args[0] == trace.ListTracesRequest() - - -@pytest.mark.asyncio -async def test_list_traces_async( - transport: str = "grpc_asyncio", request_type=trace.ListTracesRequest -): - client = TraceServiceAsyncClient( - credentials=credentials.AnonymousCredentials(), transport=transport, - ) - - # Everything is optional in proto3 as far as the runtime is concerned, - # and we are mocking out the actual API, so just send an empty request. - request = request_type() - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.list_traces), "__call__") as call: - # Designate an appropriate return value for the call. - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( - trace.ListTracesResponse(next_page_token="next_page_token_value",) - ) - - response = await client.list_traces(request) - - # Establish that the underlying gRPC stub method was called. - assert len(call.mock_calls) - _, args, _ = call.mock_calls[0] - - assert args[0] == trace.ListTracesRequest() - - # Establish that the response is the type that we expect. - assert isinstance(response, pagers.ListTracesAsyncPager) - - assert response.next_page_token == "next_page_token_value" - - -@pytest.mark.asyncio -async def test_list_traces_async_from_dict(): - await test_list_traces_async(request_type=dict) - - -def test_list_traces_flattened(): - client = TraceServiceClient(credentials=credentials.AnonymousCredentials(),) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.list_traces), "__call__") as call: - # Designate an appropriate return value for the call. - call.return_value = trace.ListTracesResponse() - - # Call the method with a truthy value for each flattened field, - # using the keyword arguments to the method. - client.list_traces(project_id="project_id_value",) - - # Establish that the underlying call was made with the expected - # request object values. - assert len(call.mock_calls) == 1 - _, args, _ = call.mock_calls[0] - - assert args[0].project_id == "project_id_value" - - -def test_list_traces_flattened_error(): - client = TraceServiceClient(credentials=credentials.AnonymousCredentials(),) - - # Attempting to call a method with both a request object and flattened - # fields is an error. - with pytest.raises(ValueError): - client.list_traces( - trace.ListTracesRequest(), project_id="project_id_value", - ) - - -@pytest.mark.asyncio -async def test_list_traces_flattened_async(): - client = TraceServiceAsyncClient(credentials=credentials.AnonymousCredentials(),) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.list_traces), "__call__") as call: - # Designate an appropriate return value for the call. - call.return_value = trace.ListTracesResponse() - - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( - trace.ListTracesResponse() - ) - # Call the method with a truthy value for each flattened field, - # using the keyword arguments to the method. - response = await client.list_traces(project_id="project_id_value",) - - # Establish that the underlying call was made with the expected - # request object values. - assert len(call.mock_calls) - _, args, _ = call.mock_calls[0] - - assert args[0].project_id == "project_id_value" - - -@pytest.mark.asyncio -async def test_list_traces_flattened_error_async(): - client = TraceServiceAsyncClient(credentials=credentials.AnonymousCredentials(),) - - # Attempting to call a method with both a request object and flattened - # fields is an error. - with pytest.raises(ValueError): - await client.list_traces( - trace.ListTracesRequest(), project_id="project_id_value", - ) - - -def test_list_traces_pager(): - client = TraceServiceClient(credentials=credentials.AnonymousCredentials,) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.list_traces), "__call__") as call: - # Set the response to a series of pages. - call.side_effect = ( - trace.ListTracesResponse( - traces=[trace.Trace(), trace.Trace(), trace.Trace(),], - next_page_token="abc", - ), - trace.ListTracesResponse(traces=[], next_page_token="def",), - trace.ListTracesResponse(traces=[trace.Trace(),], next_page_token="ghi",), - trace.ListTracesResponse(traces=[trace.Trace(), trace.Trace(),],), - RuntimeError, - ) - - metadata = () - pager = client.list_traces(request={}) - - assert pager._metadata == metadata - - results = [i for i in pager] - assert len(results) == 6 - assert all(isinstance(i, trace.Trace) for i in results) - - -def test_list_traces_pages(): - client = TraceServiceClient(credentials=credentials.AnonymousCredentials,) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.list_traces), "__call__") as call: - # Set the response to a series of pages. - call.side_effect = ( - trace.ListTracesResponse( - traces=[trace.Trace(), trace.Trace(), trace.Trace(),], - next_page_token="abc", - ), - trace.ListTracesResponse(traces=[], next_page_token="def",), - trace.ListTracesResponse(traces=[trace.Trace(),], next_page_token="ghi",), - trace.ListTracesResponse(traces=[trace.Trace(), trace.Trace(),],), - RuntimeError, - ) - pages = list(client.list_traces(request={}).pages) - for page_, token in zip(pages, ["abc", "def", "ghi", ""]): - assert page_.raw_page.next_page_token == token - - -@pytest.mark.asyncio -async def test_list_traces_async_pager(): - client = TraceServiceAsyncClient(credentials=credentials.AnonymousCredentials,) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client.transport.list_traces), "__call__", new_callable=mock.AsyncMock - ) as call: - # Set the response to a series of pages. - call.side_effect = ( - trace.ListTracesResponse( - traces=[trace.Trace(), trace.Trace(), trace.Trace(),], - next_page_token="abc", - ), - trace.ListTracesResponse(traces=[], next_page_token="def",), - trace.ListTracesResponse(traces=[trace.Trace(),], next_page_token="ghi",), - trace.ListTracesResponse(traces=[trace.Trace(), trace.Trace(),],), - RuntimeError, - ) - async_pager = await client.list_traces(request={},) - assert async_pager.next_page_token == "abc" - responses = [] - async for response in async_pager: - responses.append(response) - - assert len(responses) == 6 - assert all(isinstance(i, trace.Trace) for i in responses) - - -@pytest.mark.asyncio -async def test_list_traces_async_pages(): - client = TraceServiceAsyncClient(credentials=credentials.AnonymousCredentials,) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client.transport.list_traces), "__call__", new_callable=mock.AsyncMock - ) as call: - # Set the response to a series of pages. - call.side_effect = ( - trace.ListTracesResponse( - traces=[trace.Trace(), trace.Trace(), trace.Trace(),], - next_page_token="abc", - ), - trace.ListTracesResponse(traces=[], next_page_token="def",), - trace.ListTracesResponse(traces=[trace.Trace(),], next_page_token="ghi",), - trace.ListTracesResponse(traces=[trace.Trace(), trace.Trace(),],), - RuntimeError, - ) - pages = [] - async for page_ in (await client.list_traces(request={})).pages: - pages.append(page_) - for page_, token in zip(pages, ["abc", "def", "ghi", ""]): - assert page_.raw_page.next_page_token == token - - -def test_get_trace(transport: str = "grpc", request_type=trace.GetTraceRequest): - client = TraceServiceClient( - credentials=credentials.AnonymousCredentials(), transport=transport, - ) - - # Everything is optional in proto3 as far as the runtime is concerned, - # and we are mocking out the actual API, so just send an empty request. - request = request_type() - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.get_trace), "__call__") as call: - # Designate an appropriate return value for the call. - call.return_value = trace.Trace( - project_id="project_id_value", trace_id="trace_id_value", - ) - - response = client.get_trace(request) - - # Establish that the underlying gRPC stub method was called. - assert len(call.mock_calls) == 1 - _, args, _ = call.mock_calls[0] - - assert args[0] == trace.GetTraceRequest() - - # Establish that the response is the type that we expect. - - assert isinstance(response, trace.Trace) - - assert response.project_id == "project_id_value" - - assert response.trace_id == "trace_id_value" - - -def test_get_trace_from_dict(): - test_get_trace(request_type=dict) - - -def test_get_trace_empty_call(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = TraceServiceClient( - credentials=credentials.AnonymousCredentials(), transport="grpc", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.get_trace), "__call__") as call: - client.get_trace() - call.assert_called() - _, args, _ = call.mock_calls[0] - - assert args[0] == trace.GetTraceRequest() - - -@pytest.mark.asyncio -async def test_get_trace_async( - transport: str = "grpc_asyncio", request_type=trace.GetTraceRequest -): - client = TraceServiceAsyncClient( - credentials=credentials.AnonymousCredentials(), transport=transport, - ) - - # Everything is optional in proto3 as far as the runtime is concerned, - # and we are mocking out the actual API, so just send an empty request. - request = request_type() - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.get_trace), "__call__") as call: - # Designate an appropriate return value for the call. - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( - trace.Trace(project_id="project_id_value", trace_id="trace_id_value",) - ) - - response = await client.get_trace(request) - - # Establish that the underlying gRPC stub method was called. - assert len(call.mock_calls) - _, args, _ = call.mock_calls[0] - - assert args[0] == trace.GetTraceRequest() - - # Establish that the response is the type that we expect. - assert isinstance(response, trace.Trace) - - assert response.project_id == "project_id_value" - - assert response.trace_id == "trace_id_value" - - -@pytest.mark.asyncio -async def test_get_trace_async_from_dict(): - await test_get_trace_async(request_type=dict) - - -def test_get_trace_flattened(): - client = TraceServiceClient(credentials=credentials.AnonymousCredentials(),) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.get_trace), "__call__") as call: - # Designate an appropriate return value for the call. - call.return_value = trace.Trace() - - # Call the method with a truthy value for each flattened field, - # using the keyword arguments to the method. - client.get_trace( - project_id="project_id_value", trace_id="trace_id_value", - ) - - # Establish that the underlying call was made with the expected - # request object values. - assert len(call.mock_calls) == 1 - _, args, _ = call.mock_calls[0] - - assert args[0].project_id == "project_id_value" - - assert args[0].trace_id == "trace_id_value" - - -def test_get_trace_flattened_error(): - client = TraceServiceClient(credentials=credentials.AnonymousCredentials(),) - - # Attempting to call a method with both a request object and flattened - # fields is an error. - with pytest.raises(ValueError): - client.get_trace( - trace.GetTraceRequest(), - project_id="project_id_value", - trace_id="trace_id_value", - ) - - -@pytest.mark.asyncio -async def test_get_trace_flattened_async(): - client = TraceServiceAsyncClient(credentials=credentials.AnonymousCredentials(),) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.get_trace), "__call__") as call: - # Designate an appropriate return value for the call. - call.return_value = trace.Trace() - - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(trace.Trace()) - # Call the method with a truthy value for each flattened field, - # using the keyword arguments to the method. - response = await client.get_trace( - project_id="project_id_value", trace_id="trace_id_value", - ) - - # Establish that the underlying call was made with the expected - # request object values. - assert len(call.mock_calls) - _, args, _ = call.mock_calls[0] - - assert args[0].project_id == "project_id_value" - - assert args[0].trace_id == "trace_id_value" - - -@pytest.mark.asyncio -async def test_get_trace_flattened_error_async(): - client = TraceServiceAsyncClient(credentials=credentials.AnonymousCredentials(),) - - # Attempting to call a method with both a request object and flattened - # fields is an error. - with pytest.raises(ValueError): - await client.get_trace( - trace.GetTraceRequest(), - project_id="project_id_value", - trace_id="trace_id_value", - ) - - -def test_patch_traces(transport: str = "grpc", request_type=trace.PatchTracesRequest): - client = TraceServiceClient( - credentials=credentials.AnonymousCredentials(), transport=transport, - ) - - # Everything is optional in proto3 as far as the runtime is concerned, - # and we are mocking out the actual API, so just send an empty request. - request = request_type() - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.patch_traces), "__call__") as call: - # Designate an appropriate return value for the call. - call.return_value = None - - response = client.patch_traces(request) - - # Establish that the underlying gRPC stub method was called. - assert len(call.mock_calls) == 1 - _, args, _ = call.mock_calls[0] - - assert args[0] == trace.PatchTracesRequest() - - # Establish that the response is the type that we expect. - assert response is None - - -def test_patch_traces_from_dict(): - test_patch_traces(request_type=dict) - - -def test_patch_traces_empty_call(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = TraceServiceClient( - credentials=credentials.AnonymousCredentials(), transport="grpc", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.patch_traces), "__call__") as call: - client.patch_traces() - call.assert_called() - _, args, _ = call.mock_calls[0] - - assert args[0] == trace.PatchTracesRequest() - - -@pytest.mark.asyncio -async def test_patch_traces_async( - transport: str = "grpc_asyncio", request_type=trace.PatchTracesRequest -): - client = TraceServiceAsyncClient( - credentials=credentials.AnonymousCredentials(), transport=transport, - ) - - # Everything is optional in proto3 as far as the runtime is concerned, - # and we are mocking out the actual API, so just send an empty request. - request = request_type() - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.patch_traces), "__call__") as call: - # Designate an appropriate return value for the call. - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) - - response = await client.patch_traces(request) - - # Establish that the underlying gRPC stub method was called. - assert len(call.mock_calls) - _, args, _ = call.mock_calls[0] - - assert args[0] == trace.PatchTracesRequest() - - # Establish that the response is the type that we expect. - assert response is None - - -@pytest.mark.asyncio -async def test_patch_traces_async_from_dict(): - await test_patch_traces_async(request_type=dict) - - -def test_patch_traces_flattened(): - client = TraceServiceClient(credentials=credentials.AnonymousCredentials(),) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.patch_traces), "__call__") as call: - # Designate an appropriate return value for the call. - call.return_value = None - - # Call the method with a truthy value for each flattened field, - # using the keyword arguments to the method. - client.patch_traces( - project_id="project_id_value", - traces=trace.Traces(traces=[trace.Trace(project_id="project_id_value")]), - ) - - # Establish that the underlying call was made with the expected - # request object values. - assert len(call.mock_calls) == 1 - _, args, _ = call.mock_calls[0] - - assert args[0].project_id == "project_id_value" - - assert args[0].traces == trace.Traces( - traces=[trace.Trace(project_id="project_id_value")] - ) - - -def test_patch_traces_flattened_error(): - client = TraceServiceClient(credentials=credentials.AnonymousCredentials(),) - - # Attempting to call a method with both a request object and flattened - # fields is an error. - with pytest.raises(ValueError): - client.patch_traces( - trace.PatchTracesRequest(), - project_id="project_id_value", - traces=trace.Traces(traces=[trace.Trace(project_id="project_id_value")]), - ) - - -@pytest.mark.asyncio -async def test_patch_traces_flattened_async(): - client = TraceServiceAsyncClient(credentials=credentials.AnonymousCredentials(),) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.patch_traces), "__call__") as call: - # Designate an appropriate return value for the call. - call.return_value = None - - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) - # Call the method with a truthy value for each flattened field, - # using the keyword arguments to the method. - response = await client.patch_traces( - project_id="project_id_value", - traces=trace.Traces(traces=[trace.Trace(project_id="project_id_value")]), - ) - - # Establish that the underlying call was made with the expected - # request object values. - assert len(call.mock_calls) - _, args, _ = call.mock_calls[0] - - assert args[0].project_id == "project_id_value" - - assert args[0].traces == trace.Traces( - traces=[trace.Trace(project_id="project_id_value")] - ) - - -@pytest.mark.asyncio -async def test_patch_traces_flattened_error_async(): - client = TraceServiceAsyncClient(credentials=credentials.AnonymousCredentials(),) - - # Attempting to call a method with both a request object and flattened - # fields is an error. - with pytest.raises(ValueError): - await client.patch_traces( - trace.PatchTracesRequest(), - project_id="project_id_value", - traces=trace.Traces(traces=[trace.Trace(project_id="project_id_value")]), - ) - - -def test_credentials_transport_error(): - # It is an error to provide credentials and a transport instance. - transport = transports.TraceServiceGrpcTransport( - credentials=credentials.AnonymousCredentials(), - ) - with pytest.raises(ValueError): - client = TraceServiceClient( - credentials=credentials.AnonymousCredentials(), transport=transport, - ) - - # It is an error to provide a credentials file and a transport instance. - transport = transports.TraceServiceGrpcTransport( - credentials=credentials.AnonymousCredentials(), - ) - with pytest.raises(ValueError): - client = TraceServiceClient( - client_options={"credentials_file": "credentials.json"}, - transport=transport, - ) - - # It is an error to provide scopes and a transport instance. - transport = transports.TraceServiceGrpcTransport( - credentials=credentials.AnonymousCredentials(), - ) - with pytest.raises(ValueError): - client = TraceServiceClient( - client_options={"scopes": ["1", "2"]}, transport=transport, - ) - - -def test_transport_instance(): - # A client may be instantiated with a custom transport instance. - transport = transports.TraceServiceGrpcTransport( - credentials=credentials.AnonymousCredentials(), - ) - client = TraceServiceClient(transport=transport) - assert client.transport is transport - - -def test_transport_get_channel(): - # A client may be instantiated with a custom transport instance. - transport = transports.TraceServiceGrpcTransport( - credentials=credentials.AnonymousCredentials(), - ) - channel = transport.grpc_channel - assert channel - - transport = transports.TraceServiceGrpcAsyncIOTransport( - credentials=credentials.AnonymousCredentials(), - ) - channel = transport.grpc_channel - assert channel - - -@pytest.mark.parametrize( - "transport_class", - [ - transports.TraceServiceGrpcTransport, - transports.TraceServiceGrpcAsyncIOTransport, - ], -) -def test_transport_adc(transport_class): - # Test default credentials are used if not provided. - with mock.patch.object(auth, "default") as adc: - adc.return_value = (credentials.AnonymousCredentials(), None) - transport_class() - adc.assert_called_once() - - -def test_transport_grpc_default(): - # A client should use the gRPC transport by default. - client = TraceServiceClient(credentials=credentials.AnonymousCredentials(),) - assert isinstance(client.transport, transports.TraceServiceGrpcTransport,) - - -def test_trace_service_base_transport_error(): - # Passing both a credentials object and credentials_file should raise an error - with pytest.raises(exceptions.DuplicateCredentialArgs): - transport = transports.TraceServiceTransport( - credentials=credentials.AnonymousCredentials(), - credentials_file="credentials.json", - ) - - -def test_trace_service_base_transport(): - # Instantiate the base transport. - with mock.patch( - "google.devtools.cloudtrace_v1.services.trace_service.transports.TraceServiceTransport.__init__" - ) as Transport: - Transport.return_value = None - transport = transports.TraceServiceTransport( - credentials=credentials.AnonymousCredentials(), - ) - - # Every method on the transport should just blindly - # raise NotImplementedError. - methods = ( - "list_traces", - "get_trace", - "patch_traces", - ) - for method in methods: - with pytest.raises(NotImplementedError): - getattr(transport, method)(request=object()) - - -def test_trace_service_base_transport_with_credentials_file(): - # Instantiate the base transport with a credentials file - with mock.patch.object( - auth, "load_credentials_from_file" - ) as load_creds, mock.patch( - "google.devtools.cloudtrace_v1.services.trace_service.transports.TraceServiceTransport._prep_wrapped_messages" - ) as Transport: - Transport.return_value = None - load_creds.return_value = (credentials.AnonymousCredentials(), None) - transport = transports.TraceServiceTransport( - credentials_file="credentials.json", quota_project_id="octopus", - ) - load_creds.assert_called_once_with( - "credentials.json", - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/trace.append", - "https://www.googleapis.com/auth/trace.readonly", - ), - quota_project_id="octopus", - ) - - -def test_trace_service_base_transport_with_adc(): - # Test the default credentials are used if credentials and credentials_file are None. - with mock.patch.object(auth, "default") as adc, mock.patch( - "google.devtools.cloudtrace_v1.services.trace_service.transports.TraceServiceTransport._prep_wrapped_messages" - ) as Transport: - Transport.return_value = None - adc.return_value = (credentials.AnonymousCredentials(), None) - transport = transports.TraceServiceTransport() - adc.assert_called_once() - - -def test_trace_service_auth_adc(): - # If no credentials are provided, we should use ADC credentials. - with mock.patch.object(auth, "default") as adc: - adc.return_value = (credentials.AnonymousCredentials(), None) - TraceServiceClient() - adc.assert_called_once_with( - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/trace.append", - "https://www.googleapis.com/auth/trace.readonly", - ), - quota_project_id=None, - ) - - -def test_trace_service_transport_auth_adc(): - # If credentials and host are not provided, the transport class should use - # ADC credentials. - with mock.patch.object(auth, "default") as adc: - adc.return_value = (credentials.AnonymousCredentials(), None) - transports.TraceServiceGrpcTransport( - host="squid.clam.whelk", quota_project_id="octopus" - ) - adc.assert_called_once_with( - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/trace.append", - "https://www.googleapis.com/auth/trace.readonly", - ), - quota_project_id="octopus", - ) - - -@pytest.mark.parametrize( - "transport_class", - [transports.TraceServiceGrpcTransport, transports.TraceServiceGrpcAsyncIOTransport], -) -def test_trace_service_grpc_transport_client_cert_source_for_mtls(transport_class): - cred = credentials.AnonymousCredentials() - - # Check ssl_channel_credentials is used if provided. - with mock.patch.object(transport_class, "create_channel") as mock_create_channel: - mock_ssl_channel_creds = mock.Mock() - transport_class( - host="squid.clam.whelk", - credentials=cred, - ssl_channel_credentials=mock_ssl_channel_creds, - ) - mock_create_channel.assert_called_once_with( - "squid.clam.whelk:443", - credentials=cred, - credentials_file=None, - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/trace.append", - "https://www.googleapis.com/auth/trace.readonly", - ), - ssl_credentials=mock_ssl_channel_creds, - quota_project_id=None, - options=[ - ("grpc.max_send_message_length", -1), - ("grpc.max_receive_message_length", -1), - ], - ) - - # Check if ssl_channel_credentials is not provided, then client_cert_source_for_mtls - # is used. - with mock.patch.object(transport_class, "create_channel", return_value=mock.Mock()): - with mock.patch("grpc.ssl_channel_credentials") as mock_ssl_cred: - transport_class( - credentials=cred, - client_cert_source_for_mtls=client_cert_source_callback, - ) - expected_cert, expected_key = client_cert_source_callback() - mock_ssl_cred.assert_called_once_with( - certificate_chain=expected_cert, private_key=expected_key - ) - - -def test_trace_service_host_no_port(): - client = TraceServiceClient( - credentials=credentials.AnonymousCredentials(), - client_options=client_options.ClientOptions( - api_endpoint="cloudtrace.googleapis.com" - ), - ) - assert client.transport._host == "cloudtrace.googleapis.com:443" - - -def test_trace_service_host_with_port(): - client = TraceServiceClient( - credentials=credentials.AnonymousCredentials(), - client_options=client_options.ClientOptions( - api_endpoint="cloudtrace.googleapis.com:8000" - ), - ) - assert client.transport._host == "cloudtrace.googleapis.com:8000" - - -def test_trace_service_grpc_transport_channel(): - channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) - - # Check that channel is used if provided. - transport = transports.TraceServiceGrpcTransport( - host="squid.clam.whelk", channel=channel, - ) - assert transport.grpc_channel == channel - assert transport._host == "squid.clam.whelk:443" - assert transport._ssl_channel_credentials == None - - -def test_trace_service_grpc_asyncio_transport_channel(): - channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) - - # Check that channel is used if provided. - transport = transports.TraceServiceGrpcAsyncIOTransport( - host="squid.clam.whelk", channel=channel, - ) - assert transport.grpc_channel == channel - assert transport._host == "squid.clam.whelk:443" - assert transport._ssl_channel_credentials == None - - -# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are -# removed from grpc/grpc_asyncio transport constructor. -@pytest.mark.parametrize( - "transport_class", - [transports.TraceServiceGrpcTransport, transports.TraceServiceGrpcAsyncIOTransport], -) -def test_trace_service_transport_channel_mtls_with_client_cert_source(transport_class): - with mock.patch( - "grpc.ssl_channel_credentials", autospec=True - ) as grpc_ssl_channel_cred: - with mock.patch.object( - transport_class, "create_channel" - ) as grpc_create_channel: - mock_ssl_cred = mock.Mock() - grpc_ssl_channel_cred.return_value = mock_ssl_cred - - mock_grpc_channel = mock.Mock() - grpc_create_channel.return_value = mock_grpc_channel - - cred = credentials.AnonymousCredentials() - with pytest.warns(DeprecationWarning): - with mock.patch.object(auth, "default") as adc: - adc.return_value = (cred, None) - transport = transport_class( - host="squid.clam.whelk", - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=client_cert_source_callback, - ) - adc.assert_called_once() - - grpc_ssl_channel_cred.assert_called_once_with( - certificate_chain=b"cert bytes", private_key=b"key bytes" - ) - grpc_create_channel.assert_called_once_with( - "mtls.squid.clam.whelk:443", - credentials=cred, - credentials_file=None, - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/trace.append", - "https://www.googleapis.com/auth/trace.readonly", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - options=[ - ("grpc.max_send_message_length", -1), - ("grpc.max_receive_message_length", -1), - ], - ) - assert transport.grpc_channel == mock_grpc_channel - assert transport._ssl_channel_credentials == mock_ssl_cred - - -# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are -# removed from grpc/grpc_asyncio transport constructor. -@pytest.mark.parametrize( - "transport_class", - [transports.TraceServiceGrpcTransport, transports.TraceServiceGrpcAsyncIOTransport], -) -def test_trace_service_transport_channel_mtls_with_adc(transport_class): - mock_ssl_cred = mock.Mock() - with mock.patch.multiple( - "google.auth.transport.grpc.SslCredentials", - __init__=mock.Mock(return_value=None), - ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), - ): - with mock.patch.object( - transport_class, "create_channel" - ) as grpc_create_channel: - mock_grpc_channel = mock.Mock() - grpc_create_channel.return_value = mock_grpc_channel - mock_cred = mock.Mock() - - with pytest.warns(DeprecationWarning): - transport = transport_class( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=None, - ) - - grpc_create_channel.assert_called_once_with( - "mtls.squid.clam.whelk:443", - credentials=mock_cred, - credentials_file=None, - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/trace.append", - "https://www.googleapis.com/auth/trace.readonly", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - options=[ - ("grpc.max_send_message_length", -1), - ("grpc.max_receive_message_length", -1), - ], - ) - assert transport.grpc_channel == mock_grpc_channel - - -def test_common_billing_account_path(): - billing_account = "squid" - - expected = "billingAccounts/{billing_account}".format( - billing_account=billing_account, - ) - actual = TraceServiceClient.common_billing_account_path(billing_account) - assert expected == actual - - -def test_parse_common_billing_account_path(): - expected = { - "billing_account": "clam", - } - path = TraceServiceClient.common_billing_account_path(**expected) - - # Check that the path construction is reversible. - actual = TraceServiceClient.parse_common_billing_account_path(path) - assert expected == actual - - -def test_common_folder_path(): - folder = "whelk" - - expected = "folders/{folder}".format(folder=folder,) - actual = TraceServiceClient.common_folder_path(folder) - assert expected == actual - - -def test_parse_common_folder_path(): - expected = { - "folder": "octopus", - } - path = TraceServiceClient.common_folder_path(**expected) - - # Check that the path construction is reversible. - actual = TraceServiceClient.parse_common_folder_path(path) - assert expected == actual - - -def test_common_organization_path(): - organization = "oyster" - - expected = "organizations/{organization}".format(organization=organization,) - actual = TraceServiceClient.common_organization_path(organization) - assert expected == actual - - -def test_parse_common_organization_path(): - expected = { - "organization": "nudibranch", - } - path = TraceServiceClient.common_organization_path(**expected) - - # Check that the path construction is reversible. - actual = TraceServiceClient.parse_common_organization_path(path) - assert expected == actual - - -def test_common_project_path(): - project = "cuttlefish" - - expected = "projects/{project}".format(project=project,) - actual = TraceServiceClient.common_project_path(project) - assert expected == actual - - -def test_parse_common_project_path(): - expected = { - "project": "mussel", - } - path = TraceServiceClient.common_project_path(**expected) - - # Check that the path construction is reversible. - actual = TraceServiceClient.parse_common_project_path(path) - assert expected == actual - - -def test_common_location_path(): - project = "winkle" - location = "nautilus" - - expected = "projects/{project}/locations/{location}".format( - project=project, location=location, - ) - actual = TraceServiceClient.common_location_path(project, location) - assert expected == actual - - -def test_parse_common_location_path(): - expected = { - "project": "scallop", - "location": "abalone", - } - path = TraceServiceClient.common_location_path(**expected) - - # Check that the path construction is reversible. - actual = TraceServiceClient.parse_common_location_path(path) - assert expected == actual - - -def test_client_withDEFAULT_CLIENT_INFO(): - client_info = gapic_v1.client_info.ClientInfo() - - with mock.patch.object( - transports.TraceServiceTransport, "_prep_wrapped_messages" - ) as prep: - client = TraceServiceClient( - credentials=credentials.AnonymousCredentials(), client_info=client_info, - ) - prep.assert_called_once_with(client_info) - - with mock.patch.object( - transports.TraceServiceTransport, "_prep_wrapped_messages" - ) as prep: - transport_class = TraceServiceClient.get_transport_class() - transport = transport_class( - credentials=credentials.AnonymousCredentials(), client_info=client_info, - ) - prep.assert_called_once_with(client_info) diff --git a/tests/unit/gapic/cloudtrace_v2/__init__.py b/tests/unit/gapic/cloudtrace_v2/__init__.py deleted file mode 100644 index 42ffdf2b..00000000 --- a/tests/unit/gapic/cloudtrace_v2/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright 2020 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. -# diff --git a/tests/unit/gapic/cloudtrace_v2/test_trace_service.py b/tests/unit/gapic/cloudtrace_v2/test_trace_service.py deleted file mode 100644 index 16570b99..00000000 --- a/tests/unit/gapic/cloudtrace_v2/test_trace_service.py +++ /dev/null @@ -1,1316 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright 2020 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. -# - -import os -import mock - -import grpc -from grpc.experimental import aio -import math -import pytest -from proto.marshal.rules.dates import DurationRule, TimestampRule - -from google import auth -from google.api_core import client_options -from google.api_core import exceptions -from google.api_core import gapic_v1 -from google.api_core import grpc_helpers -from google.api_core import grpc_helpers_async -from google.auth import credentials -from google.auth.exceptions import MutualTLSChannelError -from google.devtools.cloudtrace_v2.services.trace_service import TraceServiceAsyncClient -from google.devtools.cloudtrace_v2.services.trace_service import TraceServiceClient -from google.devtools.cloudtrace_v2.services.trace_service import transports -from google.devtools.cloudtrace_v2.types import trace -from google.devtools.cloudtrace_v2.types import tracing -from google.oauth2 import service_account -from google.protobuf import any_pb2 as gp_any # type: ignore -from google.protobuf import timestamp_pb2 as timestamp # type: ignore -from google.protobuf import wrappers_pb2 as wrappers # type: ignore -from google.rpc import status_pb2 as status # type: ignore - - -def client_cert_source_callback(): - return b"cert bytes", b"key bytes" - - -# If default endpoint is localhost, then default mtls endpoint will be the same. -# This method modifies the default endpoint so the client can produce a different -# mtls endpoint for endpoint testing purposes. -def modify_default_endpoint(client): - return ( - "foo.googleapis.com" - if ("localhost" in client.DEFAULT_ENDPOINT) - else client.DEFAULT_ENDPOINT - ) - - -def test__get_default_mtls_endpoint(): - api_endpoint = "example.googleapis.com" - api_mtls_endpoint = "example.mtls.googleapis.com" - sandbox_endpoint = "example.sandbox.googleapis.com" - sandbox_mtls_endpoint = "example.mtls.sandbox.googleapis.com" - non_googleapi = "api.example.com" - - assert TraceServiceClient._get_default_mtls_endpoint(None) is None - assert ( - TraceServiceClient._get_default_mtls_endpoint(api_endpoint) == api_mtls_endpoint - ) - assert ( - TraceServiceClient._get_default_mtls_endpoint(api_mtls_endpoint) - == api_mtls_endpoint - ) - assert ( - TraceServiceClient._get_default_mtls_endpoint(sandbox_endpoint) - == sandbox_mtls_endpoint - ) - assert ( - TraceServiceClient._get_default_mtls_endpoint(sandbox_mtls_endpoint) - == sandbox_mtls_endpoint - ) - assert TraceServiceClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi - - -@pytest.mark.parametrize("client_class", [TraceServiceClient, TraceServiceAsyncClient,]) -def test_trace_service_client_from_service_account_info(client_class): - creds = credentials.AnonymousCredentials() - with mock.patch.object( - service_account.Credentials, "from_service_account_info" - ) as factory: - factory.return_value = creds - info = {"valid": True} - client = client_class.from_service_account_info(info) - assert client.transport._credentials == creds - assert isinstance(client, client_class) - - assert client.transport._host == "cloudtrace.googleapis.com:443" - - -@pytest.mark.parametrize("client_class", [TraceServiceClient, TraceServiceAsyncClient,]) -def test_trace_service_client_from_service_account_file(client_class): - creds = credentials.AnonymousCredentials() - with mock.patch.object( - service_account.Credentials, "from_service_account_file" - ) as factory: - factory.return_value = creds - client = client_class.from_service_account_file("dummy/file/path.json") - assert client.transport._credentials == creds - assert isinstance(client, client_class) - - client = client_class.from_service_account_json("dummy/file/path.json") - assert client.transport._credentials == creds - assert isinstance(client, client_class) - - assert client.transport._host == "cloudtrace.googleapis.com:443" - - -def test_trace_service_client_get_transport_class(): - transport = TraceServiceClient.get_transport_class() - available_transports = [ - transports.TraceServiceGrpcTransport, - ] - assert transport in available_transports - - transport = TraceServiceClient.get_transport_class("grpc") - assert transport == transports.TraceServiceGrpcTransport - - -@pytest.mark.parametrize( - "client_class,transport_class,transport_name", - [ - (TraceServiceClient, transports.TraceServiceGrpcTransport, "grpc"), - ( - TraceServiceAsyncClient, - transports.TraceServiceGrpcAsyncIOTransport, - "grpc_asyncio", - ), - ], -) -@mock.patch.object( - TraceServiceClient, "DEFAULT_ENDPOINT", modify_default_endpoint(TraceServiceClient) -) -@mock.patch.object( - TraceServiceAsyncClient, - "DEFAULT_ENDPOINT", - modify_default_endpoint(TraceServiceAsyncClient), -) -def test_trace_service_client_client_options( - client_class, transport_class, transport_name -): - # Check that if channel is provided we won't create a new one. - with mock.patch.object(TraceServiceClient, "get_transport_class") as gtc: - transport = transport_class(credentials=credentials.AnonymousCredentials()) - client = client_class(transport=transport) - gtc.assert_not_called() - - # Check that if channel is provided via str we will create a new one. - with mock.patch.object(TraceServiceClient, "get_transport_class") as gtc: - client = client_class(transport=transport_name) - gtc.assert_called() - - # Check the case api_endpoint is provided. - options = client_options.ClientOptions(api_endpoint="squid.clam.whelk") - with mock.patch.object(transport_class, "__init__") as patched: - patched.return_value = None - client = client_class(client_options=options) - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host="squid.clam.whelk", - scopes=None, - client_cert_source_for_mtls=None, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) - - # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is - # "never". - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}): - with mock.patch.object(transport_class, "__init__") as patched: - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=client.DEFAULT_ENDPOINT, - scopes=None, - client_cert_source_for_mtls=None, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) - - # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is - # "always". - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}): - with mock.patch.object(transport_class, "__init__") as patched: - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=client.DEFAULT_MTLS_ENDPOINT, - scopes=None, - client_cert_source_for_mtls=None, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) - - # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT has - # unsupported value. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}): - with pytest.raises(MutualTLSChannelError): - client = client_class() - - # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value. - with mock.patch.dict( - os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} - ): - with pytest.raises(ValueError): - client = client_class() - - # Check the case quota_project_id is provided - options = client_options.ClientOptions(quota_project_id="octopus") - with mock.patch.object(transport_class, "__init__") as patched: - patched.return_value = None - client = client_class(client_options=options) - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=client.DEFAULT_ENDPOINT, - scopes=None, - client_cert_source_for_mtls=None, - quota_project_id="octopus", - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) - - -@pytest.mark.parametrize( - "client_class,transport_class,transport_name,use_client_cert_env", - [ - (TraceServiceClient, transports.TraceServiceGrpcTransport, "grpc", "true"), - ( - TraceServiceAsyncClient, - transports.TraceServiceGrpcAsyncIOTransport, - "grpc_asyncio", - "true", - ), - (TraceServiceClient, transports.TraceServiceGrpcTransport, "grpc", "false"), - ( - TraceServiceAsyncClient, - transports.TraceServiceGrpcAsyncIOTransport, - "grpc_asyncio", - "false", - ), - ], -) -@mock.patch.object( - TraceServiceClient, "DEFAULT_ENDPOINT", modify_default_endpoint(TraceServiceClient) -) -@mock.patch.object( - TraceServiceAsyncClient, - "DEFAULT_ENDPOINT", - modify_default_endpoint(TraceServiceAsyncClient), -) -@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) -def test_trace_service_client_mtls_env_auto( - client_class, transport_class, transport_name, use_client_cert_env -): - # This tests the endpoint autoswitch behavior. Endpoint is autoswitched to the default - # mtls endpoint, if GOOGLE_API_USE_CLIENT_CERTIFICATE is "true" and client cert exists. - - # Check the case client_cert_source is provided. Whether client cert is used depends on - # GOOGLE_API_USE_CLIENT_CERTIFICATE value. - with mock.patch.dict( - os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} - ): - options = client_options.ClientOptions( - client_cert_source=client_cert_source_callback - ) - with mock.patch.object(transport_class, "__init__") as patched: - patched.return_value = None - client = client_class(client_options=options) - - if use_client_cert_env == "false": - expected_client_cert_source = None - expected_host = client.DEFAULT_ENDPOINT - else: - expected_client_cert_source = client_cert_source_callback - expected_host = client.DEFAULT_MTLS_ENDPOINT - - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - client_cert_source_for_mtls=expected_client_cert_source, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) - - # Check the case ADC client cert is provided. Whether client cert is used depends on - # GOOGLE_API_USE_CLIENT_CERTIFICATE value. - with mock.patch.dict( - os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} - ): - with mock.patch.object(transport_class, "__init__") as patched: - with mock.patch( - "google.auth.transport.mtls.has_default_client_cert_source", - return_value=True, - ): - with mock.patch( - "google.auth.transport.mtls.default_client_cert_source", - return_value=client_cert_source_callback, - ): - if use_client_cert_env == "false": - expected_host = client.DEFAULT_ENDPOINT - expected_client_cert_source = None - else: - expected_host = client.DEFAULT_MTLS_ENDPOINT - expected_client_cert_source = client_cert_source_callback - - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - client_cert_source_for_mtls=expected_client_cert_source, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) - - # Check the case client_cert_source and ADC client cert are not provided. - with mock.patch.dict( - os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} - ): - with mock.patch.object(transport_class, "__init__") as patched: - with mock.patch( - "google.auth.transport.mtls.has_default_client_cert_source", - return_value=False, - ): - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=client.DEFAULT_ENDPOINT, - scopes=None, - client_cert_source_for_mtls=None, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) - - -@pytest.mark.parametrize( - "client_class,transport_class,transport_name", - [ - (TraceServiceClient, transports.TraceServiceGrpcTransport, "grpc"), - ( - TraceServiceAsyncClient, - transports.TraceServiceGrpcAsyncIOTransport, - "grpc_asyncio", - ), - ], -) -def test_trace_service_client_client_options_scopes( - client_class, transport_class, transport_name -): - # Check the case scopes are provided. - options = client_options.ClientOptions(scopes=["1", "2"],) - with mock.patch.object(transport_class, "__init__") as patched: - patched.return_value = None - client = client_class(client_options=options) - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=client.DEFAULT_ENDPOINT, - scopes=["1", "2"], - client_cert_source_for_mtls=None, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) - - -@pytest.mark.parametrize( - "client_class,transport_class,transport_name", - [ - (TraceServiceClient, transports.TraceServiceGrpcTransport, "grpc"), - ( - TraceServiceAsyncClient, - transports.TraceServiceGrpcAsyncIOTransport, - "grpc_asyncio", - ), - ], -) -def test_trace_service_client_client_options_credentials_file( - client_class, transport_class, transport_name -): - # Check the case credentials file is provided. - options = client_options.ClientOptions(credentials_file="credentials.json") - with mock.patch.object(transport_class, "__init__") as patched: - patched.return_value = None - client = client_class(client_options=options) - patched.assert_called_once_with( - credentials=None, - credentials_file="credentials.json", - host=client.DEFAULT_ENDPOINT, - scopes=None, - client_cert_source_for_mtls=None, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) - - -def test_trace_service_client_client_options_from_dict(): - with mock.patch( - "google.devtools.cloudtrace_v2.services.trace_service.transports.TraceServiceGrpcTransport.__init__" - ) as grpc_transport: - grpc_transport.return_value = None - client = TraceServiceClient(client_options={"api_endpoint": "squid.clam.whelk"}) - grpc_transport.assert_called_once_with( - credentials=None, - credentials_file=None, - host="squid.clam.whelk", - scopes=None, - client_cert_source_for_mtls=None, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) - - -def test_batch_write_spans( - transport: str = "grpc", request_type=tracing.BatchWriteSpansRequest -): - client = TraceServiceClient( - credentials=credentials.AnonymousCredentials(), transport=transport, - ) - - # Everything is optional in proto3 as far as the runtime is concerned, - # and we are mocking out the actual API, so just send an empty request. - request = request_type() - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client.transport.batch_write_spans), "__call__" - ) as call: - # Designate an appropriate return value for the call. - call.return_value = None - - response = client.batch_write_spans(request) - - # Establish that the underlying gRPC stub method was called. - assert len(call.mock_calls) == 1 - _, args, _ = call.mock_calls[0] - - assert args[0] == tracing.BatchWriteSpansRequest() - - # Establish that the response is the type that we expect. - assert response is None - - -def test_batch_write_spans_from_dict(): - test_batch_write_spans(request_type=dict) - - -def test_batch_write_spans_empty_call(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = TraceServiceClient( - credentials=credentials.AnonymousCredentials(), transport="grpc", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client.transport.batch_write_spans), "__call__" - ) as call: - client.batch_write_spans() - call.assert_called() - _, args, _ = call.mock_calls[0] - - assert args[0] == tracing.BatchWriteSpansRequest() - - -@pytest.mark.asyncio -async def test_batch_write_spans_async( - transport: str = "grpc_asyncio", request_type=tracing.BatchWriteSpansRequest -): - client = TraceServiceAsyncClient( - credentials=credentials.AnonymousCredentials(), transport=transport, - ) - - # Everything is optional in proto3 as far as the runtime is concerned, - # and we are mocking out the actual API, so just send an empty request. - request = request_type() - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client.transport.batch_write_spans), "__call__" - ) as call: - # Designate an appropriate return value for the call. - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) - - response = await client.batch_write_spans(request) - - # Establish that the underlying gRPC stub method was called. - assert len(call.mock_calls) - _, args, _ = call.mock_calls[0] - - assert args[0] == tracing.BatchWriteSpansRequest() - - # Establish that the response is the type that we expect. - assert response is None - - -@pytest.mark.asyncio -async def test_batch_write_spans_async_from_dict(): - await test_batch_write_spans_async(request_type=dict) - - -def test_batch_write_spans_field_headers(): - client = TraceServiceClient(credentials=credentials.AnonymousCredentials(),) - - # Any value that is part of the HTTP/1.1 URI should be sent as - # a field header. Set these to a non-empty value. - request = tracing.BatchWriteSpansRequest() - request.name = "name/value" - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client.transport.batch_write_spans), "__call__" - ) as call: - call.return_value = None - - client.batch_write_spans(request) - - # Establish that the underlying gRPC stub method was called. - assert len(call.mock_calls) == 1 - _, args, _ = call.mock_calls[0] - assert args[0] == request - - # Establish that the field header was sent. - _, _, kw = call.mock_calls[0] - assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] - - -@pytest.mark.asyncio -async def test_batch_write_spans_field_headers_async(): - client = TraceServiceAsyncClient(credentials=credentials.AnonymousCredentials(),) - - # Any value that is part of the HTTP/1.1 URI should be sent as - # a field header. Set these to a non-empty value. - request = tracing.BatchWriteSpansRequest() - request.name = "name/value" - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client.transport.batch_write_spans), "__call__" - ) as call: - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) - - await client.batch_write_spans(request) - - # Establish that the underlying gRPC stub method was called. - assert len(call.mock_calls) - _, args, _ = call.mock_calls[0] - assert args[0] == request - - # Establish that the field header was sent. - _, _, kw = call.mock_calls[0] - assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] - - -def test_batch_write_spans_flattened(): - client = TraceServiceClient(credentials=credentials.AnonymousCredentials(),) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client.transport.batch_write_spans), "__call__" - ) as call: - # Designate an appropriate return value for the call. - call.return_value = None - - # Call the method with a truthy value for each flattened field, - # using the keyword arguments to the method. - client.batch_write_spans( - name="name_value", spans=[trace.Span(name="name_value")], - ) - - # Establish that the underlying call was made with the expected - # request object values. - assert len(call.mock_calls) == 1 - _, args, _ = call.mock_calls[0] - - assert args[0].name == "name_value" - - assert args[0].spans == [trace.Span(name="name_value")] - - -def test_batch_write_spans_flattened_error(): - client = TraceServiceClient(credentials=credentials.AnonymousCredentials(),) - - # Attempting to call a method with both a request object and flattened - # fields is an error. - with pytest.raises(ValueError): - client.batch_write_spans( - tracing.BatchWriteSpansRequest(), - name="name_value", - spans=[trace.Span(name="name_value")], - ) - - -@pytest.mark.asyncio -async def test_batch_write_spans_flattened_async(): - client = TraceServiceAsyncClient(credentials=credentials.AnonymousCredentials(),) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client.transport.batch_write_spans), "__call__" - ) as call: - # Designate an appropriate return value for the call. - call.return_value = None - - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) - # Call the method with a truthy value for each flattened field, - # using the keyword arguments to the method. - response = await client.batch_write_spans( - name="name_value", spans=[trace.Span(name="name_value")], - ) - - # Establish that the underlying call was made with the expected - # request object values. - assert len(call.mock_calls) - _, args, _ = call.mock_calls[0] - - assert args[0].name == "name_value" - - assert args[0].spans == [trace.Span(name="name_value")] - - -@pytest.mark.asyncio -async def test_batch_write_spans_flattened_error_async(): - client = TraceServiceAsyncClient(credentials=credentials.AnonymousCredentials(),) - - # Attempting to call a method with both a request object and flattened - # fields is an error. - with pytest.raises(ValueError): - await client.batch_write_spans( - tracing.BatchWriteSpansRequest(), - name="name_value", - spans=[trace.Span(name="name_value")], - ) - - -def test_create_span(transport: str = "grpc", request_type=trace.Span): - client = TraceServiceClient( - credentials=credentials.AnonymousCredentials(), transport=transport, - ) - - # Everything is optional in proto3 as far as the runtime is concerned, - # and we are mocking out the actual API, so just send an empty request. - request = request_type() - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.create_span), "__call__") as call: - # Designate an appropriate return value for the call. - call.return_value = trace.Span( - name="name_value", - span_id="span_id_value", - parent_span_id="parent_span_id_value", - span_kind=trace.Span.SpanKind.INTERNAL, - ) - - response = client.create_span(request) - - # Establish that the underlying gRPC stub method was called. - assert len(call.mock_calls) == 1 - _, args, _ = call.mock_calls[0] - - assert args[0] == trace.Span() - - # Establish that the response is the type that we expect. - - assert isinstance(response, trace.Span) - - assert response.name == "name_value" - - assert response.span_id == "span_id_value" - - assert response.parent_span_id == "parent_span_id_value" - - assert response.span_kind == trace.Span.SpanKind.INTERNAL - - -def test_create_span_from_dict(): - test_create_span(request_type=dict) - - -def test_create_span_empty_call(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = TraceServiceClient( - credentials=credentials.AnonymousCredentials(), transport="grpc", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.create_span), "__call__") as call: - client.create_span() - call.assert_called() - _, args, _ = call.mock_calls[0] - - assert args[0] == trace.Span() - - -@pytest.mark.asyncio -async def test_create_span_async( - transport: str = "grpc_asyncio", request_type=trace.Span -): - client = TraceServiceAsyncClient( - credentials=credentials.AnonymousCredentials(), transport=transport, - ) - - # Everything is optional in proto3 as far as the runtime is concerned, - # and we are mocking out the actual API, so just send an empty request. - request = request_type() - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.create_span), "__call__") as call: - # Designate an appropriate return value for the call. - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( - trace.Span( - name="name_value", - span_id="span_id_value", - parent_span_id="parent_span_id_value", - span_kind=trace.Span.SpanKind.INTERNAL, - ) - ) - - response = await client.create_span(request) - - # Establish that the underlying gRPC stub method was called. - assert len(call.mock_calls) - _, args, _ = call.mock_calls[0] - - assert args[0] == trace.Span() - - # Establish that the response is the type that we expect. - assert isinstance(response, trace.Span) - - assert response.name == "name_value" - - assert response.span_id == "span_id_value" - - assert response.parent_span_id == "parent_span_id_value" - - assert response.span_kind == trace.Span.SpanKind.INTERNAL - - -@pytest.mark.asyncio -async def test_create_span_async_from_dict(): - await test_create_span_async(request_type=dict) - - -def test_create_span_field_headers(): - client = TraceServiceClient(credentials=credentials.AnonymousCredentials(),) - - # Any value that is part of the HTTP/1.1 URI should be sent as - # a field header. Set these to a non-empty value. - request = trace.Span() - request.name = "name/value" - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.create_span), "__call__") as call: - call.return_value = trace.Span() - - client.create_span(request) - - # Establish that the underlying gRPC stub method was called. - assert len(call.mock_calls) == 1 - _, args, _ = call.mock_calls[0] - assert args[0] == request - - # Establish that the field header was sent. - _, _, kw = call.mock_calls[0] - assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] - - -@pytest.mark.asyncio -async def test_create_span_field_headers_async(): - client = TraceServiceAsyncClient(credentials=credentials.AnonymousCredentials(),) - - # Any value that is part of the HTTP/1.1 URI should be sent as - # a field header. Set these to a non-empty value. - request = trace.Span() - request.name = "name/value" - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.create_span), "__call__") as call: - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(trace.Span()) - - await client.create_span(request) - - # Establish that the underlying gRPC stub method was called. - assert len(call.mock_calls) - _, args, _ = call.mock_calls[0] - assert args[0] == request - - # Establish that the field header was sent. - _, _, kw = call.mock_calls[0] - assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] - - -def test_credentials_transport_error(): - # It is an error to provide credentials and a transport instance. - transport = transports.TraceServiceGrpcTransport( - credentials=credentials.AnonymousCredentials(), - ) - with pytest.raises(ValueError): - client = TraceServiceClient( - credentials=credentials.AnonymousCredentials(), transport=transport, - ) - - # It is an error to provide a credentials file and a transport instance. - transport = transports.TraceServiceGrpcTransport( - credentials=credentials.AnonymousCredentials(), - ) - with pytest.raises(ValueError): - client = TraceServiceClient( - client_options={"credentials_file": "credentials.json"}, - transport=transport, - ) - - # It is an error to provide scopes and a transport instance. - transport = transports.TraceServiceGrpcTransport( - credentials=credentials.AnonymousCredentials(), - ) - with pytest.raises(ValueError): - client = TraceServiceClient( - client_options={"scopes": ["1", "2"]}, transport=transport, - ) - - -def test_transport_instance(): - # A client may be instantiated with a custom transport instance. - transport = transports.TraceServiceGrpcTransport( - credentials=credentials.AnonymousCredentials(), - ) - client = TraceServiceClient(transport=transport) - assert client.transport is transport - - -def test_transport_get_channel(): - # A client may be instantiated with a custom transport instance. - transport = transports.TraceServiceGrpcTransport( - credentials=credentials.AnonymousCredentials(), - ) - channel = transport.grpc_channel - assert channel - - transport = transports.TraceServiceGrpcAsyncIOTransport( - credentials=credentials.AnonymousCredentials(), - ) - channel = transport.grpc_channel - assert channel - - -@pytest.mark.parametrize( - "transport_class", - [ - transports.TraceServiceGrpcTransport, - transports.TraceServiceGrpcAsyncIOTransport, - ], -) -def test_transport_adc(transport_class): - # Test default credentials are used if not provided. - with mock.patch.object(auth, "default") as adc: - adc.return_value = (credentials.AnonymousCredentials(), None) - transport_class() - adc.assert_called_once() - - -def test_transport_grpc_default(): - # A client should use the gRPC transport by default. - client = TraceServiceClient(credentials=credentials.AnonymousCredentials(),) - assert isinstance(client.transport, transports.TraceServiceGrpcTransport,) - - -def test_trace_service_base_transport_error(): - # Passing both a credentials object and credentials_file should raise an error - with pytest.raises(exceptions.DuplicateCredentialArgs): - transport = transports.TraceServiceTransport( - credentials=credentials.AnonymousCredentials(), - credentials_file="credentials.json", - ) - - -def test_trace_service_base_transport(): - # Instantiate the base transport. - with mock.patch( - "google.devtools.cloudtrace_v2.services.trace_service.transports.TraceServiceTransport.__init__" - ) as Transport: - Transport.return_value = None - transport = transports.TraceServiceTransport( - credentials=credentials.AnonymousCredentials(), - ) - - # Every method on the transport should just blindly - # raise NotImplementedError. - methods = ( - "batch_write_spans", - "create_span", - ) - for method in methods: - with pytest.raises(NotImplementedError): - getattr(transport, method)(request=object()) - - -def test_trace_service_base_transport_with_credentials_file(): - # Instantiate the base transport with a credentials file - with mock.patch.object( - auth, "load_credentials_from_file" - ) as load_creds, mock.patch( - "google.devtools.cloudtrace_v2.services.trace_service.transports.TraceServiceTransport._prep_wrapped_messages" - ) as Transport: - Transport.return_value = None - load_creds.return_value = (credentials.AnonymousCredentials(), None) - transport = transports.TraceServiceTransport( - credentials_file="credentials.json", quota_project_id="octopus", - ) - load_creds.assert_called_once_with( - "credentials.json", - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/trace.append", - ), - quota_project_id="octopus", - ) - - -def test_trace_service_base_transport_with_adc(): - # Test the default credentials are used if credentials and credentials_file are None. - with mock.patch.object(auth, "default") as adc, mock.patch( - "google.devtools.cloudtrace_v2.services.trace_service.transports.TraceServiceTransport._prep_wrapped_messages" - ) as Transport: - Transport.return_value = None - adc.return_value = (credentials.AnonymousCredentials(), None) - transport = transports.TraceServiceTransport() - adc.assert_called_once() - - -def test_trace_service_auth_adc(): - # If no credentials are provided, we should use ADC credentials. - with mock.patch.object(auth, "default") as adc: - adc.return_value = (credentials.AnonymousCredentials(), None) - TraceServiceClient() - adc.assert_called_once_with( - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/trace.append", - ), - quota_project_id=None, - ) - - -def test_trace_service_transport_auth_adc(): - # If credentials and host are not provided, the transport class should use - # ADC credentials. - with mock.patch.object(auth, "default") as adc: - adc.return_value = (credentials.AnonymousCredentials(), None) - transports.TraceServiceGrpcTransport( - host="squid.clam.whelk", quota_project_id="octopus" - ) - adc.assert_called_once_with( - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/trace.append", - ), - quota_project_id="octopus", - ) - - -@pytest.mark.parametrize( - "transport_class", - [transports.TraceServiceGrpcTransport, transports.TraceServiceGrpcAsyncIOTransport], -) -def test_trace_service_grpc_transport_client_cert_source_for_mtls(transport_class): - cred = credentials.AnonymousCredentials() - - # Check ssl_channel_credentials is used if provided. - with mock.patch.object(transport_class, "create_channel") as mock_create_channel: - mock_ssl_channel_creds = mock.Mock() - transport_class( - host="squid.clam.whelk", - credentials=cred, - ssl_channel_credentials=mock_ssl_channel_creds, - ) - mock_create_channel.assert_called_once_with( - "squid.clam.whelk:443", - credentials=cred, - credentials_file=None, - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/trace.append", - ), - ssl_credentials=mock_ssl_channel_creds, - quota_project_id=None, - options=[ - ("grpc.max_send_message_length", -1), - ("grpc.max_receive_message_length", -1), - ], - ) - - # Check if ssl_channel_credentials is not provided, then client_cert_source_for_mtls - # is used. - with mock.patch.object(transport_class, "create_channel", return_value=mock.Mock()): - with mock.patch("grpc.ssl_channel_credentials") as mock_ssl_cred: - transport_class( - credentials=cred, - client_cert_source_for_mtls=client_cert_source_callback, - ) - expected_cert, expected_key = client_cert_source_callback() - mock_ssl_cred.assert_called_once_with( - certificate_chain=expected_cert, private_key=expected_key - ) - - -def test_trace_service_host_no_port(): - client = TraceServiceClient( - credentials=credentials.AnonymousCredentials(), - client_options=client_options.ClientOptions( - api_endpoint="cloudtrace.googleapis.com" - ), - ) - assert client.transport._host == "cloudtrace.googleapis.com:443" - - -def test_trace_service_host_with_port(): - client = TraceServiceClient( - credentials=credentials.AnonymousCredentials(), - client_options=client_options.ClientOptions( - api_endpoint="cloudtrace.googleapis.com:8000" - ), - ) - assert client.transport._host == "cloudtrace.googleapis.com:8000" - - -def test_trace_service_grpc_transport_channel(): - channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) - - # Check that channel is used if provided. - transport = transports.TraceServiceGrpcTransport( - host="squid.clam.whelk", channel=channel, - ) - assert transport.grpc_channel == channel - assert transport._host == "squid.clam.whelk:443" - assert transport._ssl_channel_credentials == None - - -def test_trace_service_grpc_asyncio_transport_channel(): - channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) - - # Check that channel is used if provided. - transport = transports.TraceServiceGrpcAsyncIOTransport( - host="squid.clam.whelk", channel=channel, - ) - assert transport.grpc_channel == channel - assert transport._host == "squid.clam.whelk:443" - assert transport._ssl_channel_credentials == None - - -# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are -# removed from grpc/grpc_asyncio transport constructor. -@pytest.mark.parametrize( - "transport_class", - [transports.TraceServiceGrpcTransport, transports.TraceServiceGrpcAsyncIOTransport], -) -def test_trace_service_transport_channel_mtls_with_client_cert_source(transport_class): - with mock.patch( - "grpc.ssl_channel_credentials", autospec=True - ) as grpc_ssl_channel_cred: - with mock.patch.object( - transport_class, "create_channel" - ) as grpc_create_channel: - mock_ssl_cred = mock.Mock() - grpc_ssl_channel_cred.return_value = mock_ssl_cred - - mock_grpc_channel = mock.Mock() - grpc_create_channel.return_value = mock_grpc_channel - - cred = credentials.AnonymousCredentials() - with pytest.warns(DeprecationWarning): - with mock.patch.object(auth, "default") as adc: - adc.return_value = (cred, None) - transport = transport_class( - host="squid.clam.whelk", - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=client_cert_source_callback, - ) - adc.assert_called_once() - - grpc_ssl_channel_cred.assert_called_once_with( - certificate_chain=b"cert bytes", private_key=b"key bytes" - ) - grpc_create_channel.assert_called_once_with( - "mtls.squid.clam.whelk:443", - credentials=cred, - credentials_file=None, - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/trace.append", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - options=[ - ("grpc.max_send_message_length", -1), - ("grpc.max_receive_message_length", -1), - ], - ) - assert transport.grpc_channel == mock_grpc_channel - assert transport._ssl_channel_credentials == mock_ssl_cred - - -# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are -# removed from grpc/grpc_asyncio transport constructor. -@pytest.mark.parametrize( - "transport_class", - [transports.TraceServiceGrpcTransport, transports.TraceServiceGrpcAsyncIOTransport], -) -def test_trace_service_transport_channel_mtls_with_adc(transport_class): - mock_ssl_cred = mock.Mock() - with mock.patch.multiple( - "google.auth.transport.grpc.SslCredentials", - __init__=mock.Mock(return_value=None), - ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), - ): - with mock.patch.object( - transport_class, "create_channel" - ) as grpc_create_channel: - mock_grpc_channel = mock.Mock() - grpc_create_channel.return_value = mock_grpc_channel - mock_cred = mock.Mock() - - with pytest.warns(DeprecationWarning): - transport = transport_class( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=None, - ) - - grpc_create_channel.assert_called_once_with( - "mtls.squid.clam.whelk:443", - credentials=mock_cred, - credentials_file=None, - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/trace.append", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - options=[ - ("grpc.max_send_message_length", -1), - ("grpc.max_receive_message_length", -1), - ], - ) - assert transport.grpc_channel == mock_grpc_channel - - -def test_span_path(): - project = "squid" - trace = "clam" - span = "whelk" - - expected = "projects/{project}/traces/{trace}/spans/{span}".format( - project=project, trace=trace, span=span, - ) - actual = TraceServiceClient.span_path(project, trace, span) - assert expected == actual - - -def test_parse_span_path(): - expected = { - "project": "octopus", - "trace": "oyster", - "span": "nudibranch", - } - path = TraceServiceClient.span_path(**expected) - - # Check that the path construction is reversible. - actual = TraceServiceClient.parse_span_path(path) - assert expected == actual - - -def test_common_billing_account_path(): - billing_account = "cuttlefish" - - expected = "billingAccounts/{billing_account}".format( - billing_account=billing_account, - ) - actual = TraceServiceClient.common_billing_account_path(billing_account) - assert expected == actual - - -def test_parse_common_billing_account_path(): - expected = { - "billing_account": "mussel", - } - path = TraceServiceClient.common_billing_account_path(**expected) - - # Check that the path construction is reversible. - actual = TraceServiceClient.parse_common_billing_account_path(path) - assert expected == actual - - -def test_common_folder_path(): - folder = "winkle" - - expected = "folders/{folder}".format(folder=folder,) - actual = TraceServiceClient.common_folder_path(folder) - assert expected == actual - - -def test_parse_common_folder_path(): - expected = { - "folder": "nautilus", - } - path = TraceServiceClient.common_folder_path(**expected) - - # Check that the path construction is reversible. - actual = TraceServiceClient.parse_common_folder_path(path) - assert expected == actual - - -def test_common_organization_path(): - organization = "scallop" - - expected = "organizations/{organization}".format(organization=organization,) - actual = TraceServiceClient.common_organization_path(organization) - assert expected == actual - - -def test_parse_common_organization_path(): - expected = { - "organization": "abalone", - } - path = TraceServiceClient.common_organization_path(**expected) - - # Check that the path construction is reversible. - actual = TraceServiceClient.parse_common_organization_path(path) - assert expected == actual - - -def test_common_project_path(): - project = "squid" - - expected = "projects/{project}".format(project=project,) - actual = TraceServiceClient.common_project_path(project) - assert expected == actual - - -def test_parse_common_project_path(): - expected = { - "project": "clam", - } - path = TraceServiceClient.common_project_path(**expected) - - # Check that the path construction is reversible. - actual = TraceServiceClient.parse_common_project_path(path) - assert expected == actual - - -def test_common_location_path(): - project = "whelk" - location = "octopus" - - expected = "projects/{project}/locations/{location}".format( - project=project, location=location, - ) - actual = TraceServiceClient.common_location_path(project, location) - assert expected == actual - - -def test_parse_common_location_path(): - expected = { - "project": "oyster", - "location": "nudibranch", - } - path = TraceServiceClient.common_location_path(**expected) - - # Check that the path construction is reversible. - actual = TraceServiceClient.parse_common_location_path(path) - assert expected == actual - - -def test_client_withDEFAULT_CLIENT_INFO(): - client_info = gapic_v1.client_info.ClientInfo() - - with mock.patch.object( - transports.TraceServiceTransport, "_prep_wrapped_messages" - ) as prep: - client = TraceServiceClient( - credentials=credentials.AnonymousCredentials(), client_info=client_info, - ) - prep.assert_called_once_with(client_info) - - with mock.patch.object( - transports.TraceServiceTransport, "_prep_wrapped_messages" - ) as prep: - transport_class = TraceServiceClient.get_transport_class() - transport = transport_class( - credentials=credentials.AnonymousCredentials(), client_info=client_info, - ) - prep.assert_called_once_with(client_info) diff --git a/tests/unit/gapic/trace_v1/__init__.py b/tests/unit/gapic/trace_v1/__init__.py index 8b137891..42ffdf2b 100644 --- a/tests/unit/gapic/trace_v1/__init__.py +++ b/tests/unit/gapic/trace_v1/__init__.py @@ -1 +1,16 @@ +# -*- coding: utf-8 -*- +# Copyright 2020 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. +# diff --git a/tests/unit/gapic/trace_v1/test_trace_service.py b/tests/unit/gapic/trace_v1/test_trace_service.py index c5883386..779623d5 100644 --- a/tests/unit/gapic/trace_v1/test_trace_service.py +++ b/tests/unit/gapic/trace_v1/test_trace_service.py @@ -82,7 +82,22 @@ def test__get_default_mtls_endpoint(): assert TraceServiceClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi -@pytest.mark.parametrize("client_class", [TraceServiceClient, TraceServiceAsyncClient]) +@pytest.mark.parametrize("client_class", [TraceServiceClient, TraceServiceAsyncClient,]) +def test_trace_service_client_from_service_account_info(client_class): + creds = credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_info" + ) as factory: + factory.return_value = creds + info = {"valid": True} + client = client_class.from_service_account_info(info) + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "cloudtrace.googleapis.com:443" + + +@pytest.mark.parametrize("client_class", [TraceServiceClient, TraceServiceAsyncClient,]) def test_trace_service_client_from_service_account_file(client_class): creds = credentials.AnonymousCredentials() with mock.patch.object( @@ -90,17 +105,22 @@ def test_trace_service_client_from_service_account_file(client_class): ) as factory: factory.return_value = creds client = client_class.from_service_account_file("dummy/file/path.json") - assert client._transport._credentials == creds + assert client.transport._credentials == creds + assert isinstance(client, client_class) client = client_class.from_service_account_json("dummy/file/path.json") - assert client._transport._credentials == creds + assert client.transport._credentials == creds + assert isinstance(client, client_class) - assert client._transport._host == "cloudtrace.googleapis.com:443" + assert client.transport._host == "cloudtrace.googleapis.com:443" def test_trace_service_client_get_transport_class(): transport = TraceServiceClient.get_transport_class() - assert transport == transports.TraceServiceGrpcTransport + available_transports = [ + transports.TraceServiceGrpcTransport, + ] + assert transport in available_transports transport = TraceServiceClient.get_transport_class("grpc") assert transport == transports.TraceServiceGrpcTransport @@ -149,15 +169,14 @@ def test_trace_service_client_client_options( credentials_file=None, host="squid.clam.whelk", scopes=None, - api_mtls_endpoint="squid.clam.whelk", - client_cert_source=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) - # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS is + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is # "never". - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "never"}): + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}): with mock.patch.object(transport_class, "__init__") as patched: patched.return_value = None client = client_class() @@ -166,15 +185,14 @@ def test_trace_service_client_client_options( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) - # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS is + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is # "always". - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "always"}): + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}): with mock.patch.object(transport_class, "__init__") as patched: patched.return_value = None client = client_class() @@ -183,56 +201,140 @@ def test_trace_service_client_client_options( credentials_file=None, host=client.DEFAULT_MTLS_ENDPOINT, scopes=None, - api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, - client_cert_source=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) - # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is - # "auto", and client_cert_source is provided. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT has + # unsupported value. + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}): + with pytest.raises(MutualTLSChannelError): + client = client_class() + + # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} + ): + with pytest.raises(ValueError): + client = client_class() + + # Check the case quota_project_id is provided + options = client_options.ClientOptions(quota_project_id="octopus") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id="octopus", + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name,use_client_cert_env", + [ + (TraceServiceClient, transports.TraceServiceGrpcTransport, "grpc", "true"), + ( + TraceServiceAsyncClient, + transports.TraceServiceGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + (TraceServiceClient, transports.TraceServiceGrpcTransport, "grpc", "false"), + ( + TraceServiceAsyncClient, + transports.TraceServiceGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + TraceServiceClient, "DEFAULT_ENDPOINT", modify_default_endpoint(TraceServiceClient) +) +@mock.patch.object( + TraceServiceAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(TraceServiceAsyncClient), +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_trace_service_client_mtls_env_auto( + client_class, transport_class, transport_name, use_client_cert_env +): + # This tests the endpoint autoswitch behavior. Endpoint is autoswitched to the default + # mtls endpoint, if GOOGLE_API_USE_CLIENT_CERTIFICATE is "true" and client cert exists. + + # Check the case client_cert_source is provided. Whether client cert is used depends on + # GOOGLE_API_USE_CLIENT_CERTIFICATE value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): options = client_options.ClientOptions( client_cert_source=client_cert_source_callback ) with mock.patch.object(transport_class, "__init__") as patched: patched.return_value = None client = client_class(client_options=options) + + if use_client_cert_env == "false": + expected_client_cert_source = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_client_cert_source = client_cert_source_callback + expected_host = client.DEFAULT_MTLS_ENDPOINT + patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_MTLS_ENDPOINT, + host=expected_host, scopes=None, - api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, - client_cert_source=client_cert_source_callback, + client_cert_source_for_mtls=expected_client_cert_source, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) - # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is - # "auto", and default_client_cert_source is provided. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): + # Check the case ADC client cert is provided. Whether client cert is used depends on + # GOOGLE_API_USE_CLIENT_CERTIFICATE value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): with mock.patch.object(transport_class, "__init__") as patched: with mock.patch( "google.auth.transport.mtls.has_default_client_cert_source", return_value=True, ): - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=client.DEFAULT_MTLS_ENDPOINT, - scopes=None, - api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, - client_cert_source=None, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) - - # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is - # "auto", but client_cert_source and default_client_cert_source are None. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): + with mock.patch( + "google.auth.transport.mtls.default_client_cert_source", + return_value=client_cert_source_callback, + ): + if use_client_cert_env == "false": + expected_host = client.DEFAULT_ENDPOINT + expected_client_cert_source = None + else: + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_client_cert_source = client_cert_source_callback + + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): with mock.patch.object(transport_class, "__init__") as patched: with mock.patch( "google.auth.transport.mtls.has_default_client_cert_source", @@ -245,34 +347,11 @@ def test_trace_service_client_client_options( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) - # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS has - # unsupported value. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "Unsupported"}): - with pytest.raises(MutualTLSChannelError): - client = client_class() - - # Check the case quota_project_id is provided - options = client_options.ClientOptions(quota_project_id="octopus") - with mock.patch.object(transport_class, "__init__") as patched: - patched.return_value = None - client = client_class(client_options=options) - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=client.DEFAULT_ENDPOINT, - scopes=None, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, - quota_project_id="octopus", - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) - @pytest.mark.parametrize( "client_class,transport_class,transport_name", @@ -298,8 +377,7 @@ def test_trace_service_client_client_options_scopes( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=["1", "2"], - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -329,8 +407,7 @@ def test_trace_service_client_client_options_credentials_file( credentials_file="credentials.json", host=client.DEFAULT_ENDPOINT, scopes=None, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -347,8 +424,7 @@ def test_trace_service_client_client_options_from_dict(): credentials_file=None, host="squid.clam.whelk", scopes=None, - api_mtls_endpoint="squid.clam.whelk", - client_cert_source=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -364,7 +440,7 @@ def test_list_traces(transport: str = "grpc", request_type=trace.ListTracesReque request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.list_traces), "__call__") as call: + with mock.patch.object(type(client.transport.list_traces), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = trace.ListTracesResponse( next_page_token="next_page_token_value", @@ -379,6 +455,7 @@ def test_list_traces(transport: str = "grpc", request_type=trace.ListTracesReque assert args[0] == trace.ListTracesRequest() # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListTracesPager) assert response.next_page_token == "next_page_token_value" @@ -388,20 +465,36 @@ def test_list_traces_from_dict(): test_list_traces(request_type=dict) +def test_list_traces_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = TraceServiceClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_traces), "__call__") as call: + client.list_traces() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == trace.ListTracesRequest() + + @pytest.mark.asyncio -async def test_list_traces_async(transport: str = "grpc_asyncio"): +async def test_list_traces_async( + transport: str = "grpc_asyncio", request_type=trace.ListTracesRequest +): client = TraceServiceAsyncClient( credentials=credentials.AnonymousCredentials(), transport=transport, ) # Everything is optional in proto3 as far as the runtime is concerned, # and we are mocking out the actual API, so just send an empty request. - request = trace.ListTracesRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.list_traces), "__call__" - ) as call: + with mock.patch.object(type(client.transport.list_traces), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( trace.ListTracesResponse(next_page_token="next_page_token_value",) @@ -413,7 +506,7 @@ async def test_list_traces_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == trace.ListTracesRequest() # Establish that the response is the type that we expect. assert isinstance(response, pagers.ListTracesAsyncPager) @@ -421,11 +514,16 @@ async def test_list_traces_async(transport: str = "grpc_asyncio"): assert response.next_page_token == "next_page_token_value" +@pytest.mark.asyncio +async def test_list_traces_async_from_dict(): + await test_list_traces_async(request_type=dict) + + def test_list_traces_flattened(): client = TraceServiceClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.list_traces), "__call__") as call: + with mock.patch.object(type(client.transport.list_traces), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = trace.ListTracesResponse() @@ -457,9 +555,7 @@ async def test_list_traces_flattened_async(): client = TraceServiceAsyncClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.list_traces), "__call__" - ) as call: + with mock.patch.object(type(client.transport.list_traces), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = trace.ListTracesResponse() @@ -494,7 +590,7 @@ def test_list_traces_pager(): client = TraceServiceClient(credentials=credentials.AnonymousCredentials,) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.list_traces), "__call__") as call: + with mock.patch.object(type(client.transport.list_traces), "__call__") as call: # Set the response to a series of pages. call.side_effect = ( trace.ListTracesResponse( @@ -521,7 +617,7 @@ def test_list_traces_pages(): client = TraceServiceClient(credentials=credentials.AnonymousCredentials,) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.list_traces), "__call__") as call: + with mock.patch.object(type(client.transport.list_traces), "__call__") as call: # Set the response to a series of pages. call.side_effect = ( trace.ListTracesResponse( @@ -544,9 +640,7 @@ async def test_list_traces_async_pager(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.list_traces), - "__call__", - new_callable=mock.AsyncMock, + type(client.transport.list_traces), "__call__", new_callable=mock.AsyncMock ) as call: # Set the response to a series of pages. call.side_effect = ( @@ -575,9 +669,7 @@ async def test_list_traces_async_pages(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.list_traces), - "__call__", - new_callable=mock.AsyncMock, + type(client.transport.list_traces), "__call__", new_callable=mock.AsyncMock ) as call: # Set the response to a series of pages. call.side_effect = ( @@ -607,7 +699,7 @@ def test_get_trace(transport: str = "grpc", request_type=trace.GetTraceRequest): request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.get_trace), "__call__") as call: + with mock.patch.object(type(client.transport.get_trace), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = trace.Trace( project_id="project_id_value", trace_id="trace_id_value", @@ -622,6 +714,7 @@ def test_get_trace(transport: str = "grpc", request_type=trace.GetTraceRequest): assert args[0] == trace.GetTraceRequest() # Establish that the response is the type that we expect. + assert isinstance(response, trace.Trace) assert response.project_id == "project_id_value" @@ -633,20 +726,36 @@ def test_get_trace_from_dict(): test_get_trace(request_type=dict) +def test_get_trace_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = TraceServiceClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_trace), "__call__") as call: + client.get_trace() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == trace.GetTraceRequest() + + @pytest.mark.asyncio -async def test_get_trace_async(transport: str = "grpc_asyncio"): +async def test_get_trace_async( + transport: str = "grpc_asyncio", request_type=trace.GetTraceRequest +): client = TraceServiceAsyncClient( credentials=credentials.AnonymousCredentials(), transport=transport, ) # Everything is optional in proto3 as far as the runtime is concerned, # and we are mocking out the actual API, so just send an empty request. - request = trace.GetTraceRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.get_trace), "__call__" - ) as call: + with mock.patch.object(type(client.transport.get_trace), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( trace.Trace(project_id="project_id_value", trace_id="trace_id_value",) @@ -658,7 +767,7 @@ async def test_get_trace_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == trace.GetTraceRequest() # Establish that the response is the type that we expect. assert isinstance(response, trace.Trace) @@ -668,11 +777,16 @@ async def test_get_trace_async(transport: str = "grpc_asyncio"): assert response.trace_id == "trace_id_value" +@pytest.mark.asyncio +async def test_get_trace_async_from_dict(): + await test_get_trace_async(request_type=dict) + + def test_get_trace_flattened(): client = TraceServiceClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.get_trace), "__call__") as call: + with mock.patch.object(type(client.transport.get_trace), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = trace.Trace() @@ -710,9 +824,7 @@ async def test_get_trace_flattened_async(): client = TraceServiceAsyncClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.get_trace), "__call__" - ) as call: + with mock.patch.object(type(client.transport.get_trace), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = trace.Trace() @@ -757,7 +869,7 @@ def test_patch_traces(transport: str = "grpc", request_type=trace.PatchTracesReq request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.patch_traces), "__call__") as call: + with mock.patch.object(type(client.transport.patch_traces), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = None @@ -777,20 +889,36 @@ def test_patch_traces_from_dict(): test_patch_traces(request_type=dict) +def test_patch_traces_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = TraceServiceClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.patch_traces), "__call__") as call: + client.patch_traces() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == trace.PatchTracesRequest() + + @pytest.mark.asyncio -async def test_patch_traces_async(transport: str = "grpc_asyncio"): +async def test_patch_traces_async( + transport: str = "grpc_asyncio", request_type=trace.PatchTracesRequest +): client = TraceServiceAsyncClient( credentials=credentials.AnonymousCredentials(), transport=transport, ) # Everything is optional in proto3 as far as the runtime is concerned, # and we are mocking out the actual API, so just send an empty request. - request = trace.PatchTracesRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.patch_traces), "__call__" - ) as call: + with mock.patch.object(type(client.transport.patch_traces), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) @@ -800,17 +928,22 @@ async def test_patch_traces_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == trace.PatchTracesRequest() # Establish that the response is the type that we expect. assert response is None +@pytest.mark.asyncio +async def test_patch_traces_async_from_dict(): + await test_patch_traces_async(request_type=dict) + + def test_patch_traces_flattened(): client = TraceServiceClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.patch_traces), "__call__") as call: + with mock.patch.object(type(client.transport.patch_traces), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = None @@ -851,9 +984,7 @@ async def test_patch_traces_flattened_async(): client = TraceServiceAsyncClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.patch_traces), "__call__" - ) as call: + with mock.patch.object(type(client.transport.patch_traces), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = None @@ -927,7 +1058,7 @@ def test_transport_instance(): credentials=credentials.AnonymousCredentials(), ) client = TraceServiceClient(transport=transport) - assert client._transport is transport + assert client.transport is transport def test_transport_get_channel(): @@ -945,10 +1076,25 @@ def test_transport_get_channel(): assert channel +@pytest.mark.parametrize( + "transport_class", + [ + transports.TraceServiceGrpcTransport, + transports.TraceServiceGrpcAsyncIOTransport, + ], +) +def test_transport_adc(transport_class): + # Test default credentials are used if not provided. + with mock.patch.object(auth, "default") as adc: + adc.return_value = (credentials.AnonymousCredentials(), None) + transport_class() + adc.assert_called_once() + + def test_transport_grpc_default(): # A client should use the gRPC transport by default. client = TraceServiceClient(credentials=credentials.AnonymousCredentials(),) - assert isinstance(client._transport, transports.TraceServiceGrpcTransport,) + assert isinstance(client.transport, transports.TraceServiceGrpcTransport,) def test_trace_service_base_transport_error(): @@ -1005,6 +1151,17 @@ def test_trace_service_base_transport_with_credentials_file(): ) +def test_trace_service_base_transport_with_adc(): + # Test the default credentials are used if credentials and credentials_file are None. + with mock.patch.object(auth, "default") as adc, mock.patch( + "google.cloud.trace_v1.services.trace_service.transports.TraceServiceTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.TraceServiceTransport() + adc.assert_called_once() + + def test_trace_service_auth_adc(): # If no credentials are provided, we should use ADC credentials. with mock.patch.object(auth, "default") as adc: @@ -1038,6 +1195,52 @@ def test_trace_service_transport_auth_adc(): ) +@pytest.mark.parametrize( + "transport_class", + [transports.TraceServiceGrpcTransport, transports.TraceServiceGrpcAsyncIOTransport], +) +def test_trace_service_grpc_transport_client_cert_source_for_mtls(transport_class): + cred = credentials.AnonymousCredentials() + + # Check ssl_channel_credentials is used if provided. + with mock.patch.object(transport_class, "create_channel") as mock_create_channel: + mock_ssl_channel_creds = mock.Mock() + transport_class( + host="squid.clam.whelk", + credentials=cred, + ssl_channel_credentials=mock_ssl_channel_creds, + ) + mock_create_channel.assert_called_once_with( + "squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/trace.append", + "https://www.googleapis.com/auth/trace.readonly", + ), + ssl_credentials=mock_ssl_channel_creds, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Check if ssl_channel_credentials is not provided, then client_cert_source_for_mtls + # is used. + with mock.patch.object(transport_class, "create_channel", return_value=mock.Mock()): + with mock.patch("grpc.ssl_channel_credentials") as mock_ssl_cred: + transport_class( + credentials=cred, + client_cert_source_for_mtls=client_cert_source_callback, + ) + expected_cert, expected_key = client_cert_source_callback() + mock_ssl_cred.assert_called_once_with( + certificate_chain=expected_cert, private_key=expected_key + ) + + def test_trace_service_host_no_port(): client = TraceServiceClient( credentials=credentials.AnonymousCredentials(), @@ -1045,7 +1248,7 @@ def test_trace_service_host_no_port(): api_endpoint="cloudtrace.googleapis.com" ), ) - assert client._transport._host == "cloudtrace.googleapis.com:443" + assert client.transport._host == "cloudtrace.googleapis.com:443" def test_trace_service_host_with_port(): @@ -1055,201 +1258,232 @@ def test_trace_service_host_with_port(): api_endpoint="cloudtrace.googleapis.com:8000" ), ) - assert client._transport._host == "cloudtrace.googleapis.com:8000" + assert client.transport._host == "cloudtrace.googleapis.com:8000" def test_trace_service_grpc_transport_channel(): - channel = grpc.insecure_channel("http://localhost/") + channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) - # Check that if channel is provided, mtls endpoint and client_cert_source - # won't be used. - callback = mock.MagicMock() + # Check that channel is used if provided. transport = transports.TraceServiceGrpcTransport( - host="squid.clam.whelk", - channel=channel, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=callback, + host="squid.clam.whelk", channel=channel, ) assert transport.grpc_channel == channel assert transport._host == "squid.clam.whelk:443" - assert not callback.called + assert transport._ssl_channel_credentials == None def test_trace_service_grpc_asyncio_transport_channel(): - channel = aio.insecure_channel("http://localhost/") + channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) - # Check that if channel is provided, mtls endpoint and client_cert_source - # won't be used. - callback = mock.MagicMock() + # Check that channel is used if provided. transport = transports.TraceServiceGrpcAsyncIOTransport( - host="squid.clam.whelk", - channel=channel, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=callback, + host="squid.clam.whelk", channel=channel, ) assert transport.grpc_channel == channel assert transport._host == "squid.clam.whelk:443" - assert not callback.called + assert transport._ssl_channel_credentials == None -@mock.patch("grpc.ssl_channel_credentials", autospec=True) -@mock.patch("google.api_core.grpc_helpers.create_channel", autospec=True) -def test_trace_service_grpc_transport_channel_mtls_with_client_cert_source( - grpc_create_channel, grpc_ssl_channel_cred -): - # Check that if channel is None, but api_mtls_endpoint and client_cert_source - # are provided, then a mTLS channel will be created. - mock_cred = mock.Mock() +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. +@pytest.mark.parametrize( + "transport_class", + [transports.TraceServiceGrpcTransport, transports.TraceServiceGrpcAsyncIOTransport], +) +def test_trace_service_transport_channel_mtls_with_client_cert_source(transport_class): + with mock.patch( + "grpc.ssl_channel_credentials", autospec=True + ) as grpc_ssl_channel_cred: + with mock.patch.object( + transport_class, "create_channel" + ) as grpc_create_channel: + mock_ssl_cred = mock.Mock() + grpc_ssl_channel_cred.return_value = mock_ssl_cred + + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + + cred = credentials.AnonymousCredentials() + with pytest.warns(DeprecationWarning): + with mock.patch.object(auth, "default") as adc: + adc.return_value = (cred, None) + transport = transport_class( + host="squid.clam.whelk", + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=client_cert_source_callback, + ) + adc.assert_called_once() + + grpc_ssl_channel_cred.assert_called_once_with( + certificate_chain=b"cert bytes", private_key=b"key bytes" + ) + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/trace.append", + "https://www.googleapis.com/auth/trace.readonly", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred + +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. +@pytest.mark.parametrize( + "transport_class", + [transports.TraceServiceGrpcTransport, transports.TraceServiceGrpcAsyncIOTransport], +) +def test_trace_service_transport_channel_mtls_with_adc(transport_class): mock_ssl_cred = mock.Mock() - grpc_ssl_channel_cred.return_value = mock_ssl_cred + with mock.patch.multiple( + "google.auth.transport.grpc.SslCredentials", + __init__=mock.Mock(return_value=None), + ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), + ): + with mock.patch.object( + transport_class, "create_channel" + ) as grpc_create_channel: + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + mock_cred = mock.Mock() + + with pytest.warns(DeprecationWarning): + transport = transport_class( + host="squid.clam.whelk", + credentials=mock_cred, + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=None, + ) - mock_grpc_channel = mock.Mock() - grpc_create_channel.return_value = mock_grpc_channel + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=mock_cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/trace.append", + "https://www.googleapis.com/auth/trace.readonly", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + assert transport.grpc_channel == mock_grpc_channel - transport = transports.TraceServiceGrpcTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=client_cert_source_callback, - ) - grpc_ssl_channel_cred.assert_called_once_with( - certificate_chain=b"cert bytes", private_key=b"key bytes" - ) - grpc_create_channel.assert_called_once_with( - "mtls.squid.clam.whelk:443", - credentials=mock_cred, - credentials_file=None, - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/trace.append", - "https://www.googleapis.com/auth/trace.readonly", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, + +def test_common_billing_account_path(): + billing_account = "squid" + + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, ) - assert transport.grpc_channel == mock_grpc_channel + actual = TraceServiceClient.common_billing_account_path(billing_account) + assert expected == actual -@mock.patch("grpc.ssl_channel_credentials", autospec=True) -@mock.patch("google.api_core.grpc_helpers_async.create_channel", autospec=True) -def test_trace_service_grpc_asyncio_transport_channel_mtls_with_client_cert_source( - grpc_create_channel, grpc_ssl_channel_cred -): - # Check that if channel is None, but api_mtls_endpoint and client_cert_source - # are provided, then a mTLS channel will be created. - mock_cred = mock.Mock() +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "clam", + } + path = TraceServiceClient.common_billing_account_path(**expected) - mock_ssl_cred = mock.Mock() - grpc_ssl_channel_cred.return_value = mock_ssl_cred + # Check that the path construction is reversible. + actual = TraceServiceClient.parse_common_billing_account_path(path) + assert expected == actual - mock_grpc_channel = mock.Mock() - grpc_create_channel.return_value = mock_grpc_channel - transport = transports.TraceServiceGrpcAsyncIOTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=client_cert_source_callback, - ) - grpc_ssl_channel_cred.assert_called_once_with( - certificate_chain=b"cert bytes", private_key=b"key bytes" - ) - grpc_create_channel.assert_called_once_with( - "mtls.squid.clam.whelk:443", - credentials=mock_cred, - credentials_file=None, - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/trace.append", - "https://www.googleapis.com/auth/trace.readonly", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel +def test_common_folder_path(): + folder = "whelk" + expected = "folders/{folder}".format(folder=folder,) + actual = TraceServiceClient.common_folder_path(folder) + assert expected == actual -@pytest.mark.parametrize( - "api_mtls_endpoint", ["mtls.squid.clam.whelk", "mtls.squid.clam.whelk:443"] -) -@mock.patch("google.api_core.grpc_helpers.create_channel", autospec=True) -def test_trace_service_grpc_transport_channel_mtls_with_adc( - grpc_create_channel, api_mtls_endpoint -): - # Check that if channel and client_cert_source are None, but api_mtls_endpoint - # is provided, then a mTLS channel will be created with SSL ADC. - mock_grpc_channel = mock.Mock() - grpc_create_channel.return_value = mock_grpc_channel - # Mock google.auth.transport.grpc.SslCredentials class. - mock_ssl_cred = mock.Mock() - with mock.patch.multiple( - "google.auth.transport.grpc.SslCredentials", - __init__=mock.Mock(return_value=None), - ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), - ): - mock_cred = mock.Mock() - transport = transports.TraceServiceGrpcTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint=api_mtls_endpoint, - client_cert_source=None, - ) - grpc_create_channel.assert_called_once_with( - "mtls.squid.clam.whelk:443", - credentials=mock_cred, - credentials_file=None, - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/trace.append", - "https://www.googleapis.com/auth/trace.readonly", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel +def test_parse_common_folder_path(): + expected = { + "folder": "octopus", + } + path = TraceServiceClient.common_folder_path(**expected) + # Check that the path construction is reversible. + actual = TraceServiceClient.parse_common_folder_path(path) + assert expected == actual -@pytest.mark.parametrize( - "api_mtls_endpoint", ["mtls.squid.clam.whelk", "mtls.squid.clam.whelk:443"] -) -@mock.patch("google.api_core.grpc_helpers_async.create_channel", autospec=True) -def test_trace_service_grpc_asyncio_transport_channel_mtls_with_adc( - grpc_create_channel, api_mtls_endpoint -): - # Check that if channel and client_cert_source are None, but api_mtls_endpoint - # is provided, then a mTLS channel will be created with SSL ADC. - mock_grpc_channel = mock.Mock() - grpc_create_channel.return_value = mock_grpc_channel - # Mock google.auth.transport.grpc.SslCredentials class. - mock_ssl_cred = mock.Mock() - with mock.patch.multiple( - "google.auth.transport.grpc.SslCredentials", - __init__=mock.Mock(return_value=None), - ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), - ): - mock_cred = mock.Mock() - transport = transports.TraceServiceGrpcAsyncIOTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint=api_mtls_endpoint, - client_cert_source=None, - ) - grpc_create_channel.assert_called_once_with( - "mtls.squid.clam.whelk:443", - credentials=mock_cred, - credentials_file=None, - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/trace.append", - "https://www.googleapis.com/auth/trace.readonly", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel +def test_common_organization_path(): + organization = "oyster" + + expected = "organizations/{organization}".format(organization=organization,) + actual = TraceServiceClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "nudibranch", + } + path = TraceServiceClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = TraceServiceClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "cuttlefish" + + expected = "projects/{project}".format(project=project,) + actual = TraceServiceClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "mussel", + } + path = TraceServiceClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = TraceServiceClient.parse_common_project_path(path) + assert expected == actual + + +def test_common_location_path(): + project = "winkle" + location = "nautilus" + + expected = "projects/{project}/locations/{location}".format( + project=project, location=location, + ) + actual = TraceServiceClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "scallop", + "location": "abalone", + } + path = TraceServiceClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = TraceServiceClient.parse_common_location_path(path) + assert expected == actual def test_client_withDEFAULT_CLIENT_INFO(): diff --git a/tests/unit/gapic/trace_v2/__init__.py b/tests/unit/gapic/trace_v2/__init__.py index 8b137891..42ffdf2b 100644 --- a/tests/unit/gapic/trace_v2/__init__.py +++ b/tests/unit/gapic/trace_v2/__init__.py @@ -1 +1,16 @@ +# -*- coding: utf-8 -*- +# Copyright 2020 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. +# diff --git a/tests/unit/gapic/trace_v2/test_trace_service.py b/tests/unit/gapic/trace_v2/test_trace_service.py index 9072a594..d25e90cc 100644 --- a/tests/unit/gapic/trace_v2/test_trace_service.py +++ b/tests/unit/gapic/trace_v2/test_trace_service.py @@ -38,10 +38,9 @@ from google.cloud.trace_v2.types import trace from google.cloud.trace_v2.types import tracing from google.oauth2 import service_account -from google.protobuf import any_pb2 as any # type: ignore +from google.protobuf import any_pb2 as gp_any # type: ignore from google.protobuf import timestamp_pb2 as timestamp # type: ignore from google.protobuf import wrappers_pb2 as wrappers # type: ignore -from google.rpc import status_pb2 as gr_status # type: ignore from google.rpc import status_pb2 as status # type: ignore @@ -86,7 +85,22 @@ def test__get_default_mtls_endpoint(): assert TraceServiceClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi -@pytest.mark.parametrize("client_class", [TraceServiceClient, TraceServiceAsyncClient]) +@pytest.mark.parametrize("client_class", [TraceServiceClient, TraceServiceAsyncClient,]) +def test_trace_service_client_from_service_account_info(client_class): + creds = credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_info" + ) as factory: + factory.return_value = creds + info = {"valid": True} + client = client_class.from_service_account_info(info) + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "cloudtrace.googleapis.com:443" + + +@pytest.mark.parametrize("client_class", [TraceServiceClient, TraceServiceAsyncClient,]) def test_trace_service_client_from_service_account_file(client_class): creds = credentials.AnonymousCredentials() with mock.patch.object( @@ -94,17 +108,22 @@ def test_trace_service_client_from_service_account_file(client_class): ) as factory: factory.return_value = creds client = client_class.from_service_account_file("dummy/file/path.json") - assert client._transport._credentials == creds + assert client.transport._credentials == creds + assert isinstance(client, client_class) client = client_class.from_service_account_json("dummy/file/path.json") - assert client._transport._credentials == creds + assert client.transport._credentials == creds + assert isinstance(client, client_class) - assert client._transport._host == "cloudtrace.googleapis.com:443" + assert client.transport._host == "cloudtrace.googleapis.com:443" def test_trace_service_client_get_transport_class(): transport = TraceServiceClient.get_transport_class() - assert transport == transports.TraceServiceGrpcTransport + available_transports = [ + transports.TraceServiceGrpcTransport, + ] + assert transport in available_transports transport = TraceServiceClient.get_transport_class("grpc") assert transport == transports.TraceServiceGrpcTransport @@ -153,15 +172,14 @@ def test_trace_service_client_client_options( credentials_file=None, host="squid.clam.whelk", scopes=None, - api_mtls_endpoint="squid.clam.whelk", - client_cert_source=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) - # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS is + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is # "never". - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "never"}): + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}): with mock.patch.object(transport_class, "__init__") as patched: patched.return_value = None client = client_class() @@ -170,15 +188,14 @@ def test_trace_service_client_client_options( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) - # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS is + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is # "always". - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "always"}): + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}): with mock.patch.object(transport_class, "__init__") as patched: patched.return_value = None client = client_class() @@ -187,56 +204,140 @@ def test_trace_service_client_client_options( credentials_file=None, host=client.DEFAULT_MTLS_ENDPOINT, scopes=None, - api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, - client_cert_source=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) - # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is - # "auto", and client_cert_source is provided. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT has + # unsupported value. + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}): + with pytest.raises(MutualTLSChannelError): + client = client_class() + + # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} + ): + with pytest.raises(ValueError): + client = client_class() + + # Check the case quota_project_id is provided + options = client_options.ClientOptions(quota_project_id="octopus") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id="octopus", + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name,use_client_cert_env", + [ + (TraceServiceClient, transports.TraceServiceGrpcTransport, "grpc", "true"), + ( + TraceServiceAsyncClient, + transports.TraceServiceGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + (TraceServiceClient, transports.TraceServiceGrpcTransport, "grpc", "false"), + ( + TraceServiceAsyncClient, + transports.TraceServiceGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + TraceServiceClient, "DEFAULT_ENDPOINT", modify_default_endpoint(TraceServiceClient) +) +@mock.patch.object( + TraceServiceAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(TraceServiceAsyncClient), +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_trace_service_client_mtls_env_auto( + client_class, transport_class, transport_name, use_client_cert_env +): + # This tests the endpoint autoswitch behavior. Endpoint is autoswitched to the default + # mtls endpoint, if GOOGLE_API_USE_CLIENT_CERTIFICATE is "true" and client cert exists. + + # Check the case client_cert_source is provided. Whether client cert is used depends on + # GOOGLE_API_USE_CLIENT_CERTIFICATE value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): options = client_options.ClientOptions( client_cert_source=client_cert_source_callback ) with mock.patch.object(transport_class, "__init__") as patched: patched.return_value = None client = client_class(client_options=options) + + if use_client_cert_env == "false": + expected_client_cert_source = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_client_cert_source = client_cert_source_callback + expected_host = client.DEFAULT_MTLS_ENDPOINT + patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_MTLS_ENDPOINT, + host=expected_host, scopes=None, - api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, - client_cert_source=client_cert_source_callback, + client_cert_source_for_mtls=expected_client_cert_source, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) - # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is - # "auto", and default_client_cert_source is provided. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): + # Check the case ADC client cert is provided. Whether client cert is used depends on + # GOOGLE_API_USE_CLIENT_CERTIFICATE value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): with mock.patch.object(transport_class, "__init__") as patched: with mock.patch( "google.auth.transport.mtls.has_default_client_cert_source", return_value=True, ): - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=client.DEFAULT_MTLS_ENDPOINT, - scopes=None, - api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, - client_cert_source=None, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) - - # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is - # "auto", but client_cert_source and default_client_cert_source are None. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): + with mock.patch( + "google.auth.transport.mtls.default_client_cert_source", + return_value=client_cert_source_callback, + ): + if use_client_cert_env == "false": + expected_host = client.DEFAULT_ENDPOINT + expected_client_cert_source = None + else: + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_client_cert_source = client_cert_source_callback + + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): with mock.patch.object(transport_class, "__init__") as patched: with mock.patch( "google.auth.transport.mtls.has_default_client_cert_source", @@ -249,34 +350,11 @@ def test_trace_service_client_client_options( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) - # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS has - # unsupported value. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "Unsupported"}): - with pytest.raises(MutualTLSChannelError): - client = client_class() - - # Check the case quota_project_id is provided - options = client_options.ClientOptions(quota_project_id="octopus") - with mock.patch.object(transport_class, "__init__") as patched: - patched.return_value = None - client = client_class(client_options=options) - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=client.DEFAULT_ENDPOINT, - scopes=None, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, - quota_project_id="octopus", - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) - @pytest.mark.parametrize( "client_class,transport_class,transport_name", @@ -302,8 +380,7 @@ def test_trace_service_client_client_options_scopes( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=["1", "2"], - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -333,8 +410,7 @@ def test_trace_service_client_client_options_credentials_file( credentials_file="credentials.json", host=client.DEFAULT_ENDPOINT, scopes=None, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -351,8 +427,7 @@ def test_trace_service_client_client_options_from_dict(): credentials_file=None, host="squid.clam.whelk", scopes=None, - api_mtls_endpoint="squid.clam.whelk", - client_cert_source=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -371,7 +446,7 @@ def test_batch_write_spans( # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.batch_write_spans), "__call__" + type(client.transport.batch_write_spans), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = None @@ -392,19 +467,39 @@ def test_batch_write_spans_from_dict(): test_batch_write_spans(request_type=dict) +def test_batch_write_spans_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = TraceServiceClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.batch_write_spans), "__call__" + ) as call: + client.batch_write_spans() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == tracing.BatchWriteSpansRequest() + + @pytest.mark.asyncio -async def test_batch_write_spans_async(transport: str = "grpc_asyncio"): +async def test_batch_write_spans_async( + transport: str = "grpc_asyncio", request_type=tracing.BatchWriteSpansRequest +): client = TraceServiceAsyncClient( credentials=credentials.AnonymousCredentials(), transport=transport, ) # Everything is optional in proto3 as far as the runtime is concerned, # and we are mocking out the actual API, so just send an empty request. - request = tracing.BatchWriteSpansRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.batch_write_spans), "__call__" + type(client.transport.batch_write_spans), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) @@ -415,12 +510,17 @@ async def test_batch_write_spans_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == tracing.BatchWriteSpansRequest() # Establish that the response is the type that we expect. assert response is None +@pytest.mark.asyncio +async def test_batch_write_spans_async_from_dict(): + await test_batch_write_spans_async(request_type=dict) + + def test_batch_write_spans_field_headers(): client = TraceServiceClient(credentials=credentials.AnonymousCredentials(),) @@ -431,7 +531,7 @@ def test_batch_write_spans_field_headers(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.batch_write_spans), "__call__" + type(client.transport.batch_write_spans), "__call__" ) as call: call.return_value = None @@ -458,7 +558,7 @@ async def test_batch_write_spans_field_headers_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.batch_write_spans), "__call__" + type(client.transport.batch_write_spans), "__call__" ) as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) @@ -479,7 +579,7 @@ def test_batch_write_spans_flattened(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.batch_write_spans), "__call__" + type(client.transport.batch_write_spans), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = None @@ -519,7 +619,7 @@ async def test_batch_write_spans_flattened_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.batch_write_spans), "__call__" + type(client.transport.batch_write_spans), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = None @@ -565,7 +665,7 @@ def test_create_span(transport: str = "grpc", request_type=trace.Span): request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.create_span), "__call__") as call: + with mock.patch.object(type(client.transport.create_span), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = trace.Span( name="name_value", @@ -583,6 +683,7 @@ def test_create_span(transport: str = "grpc", request_type=trace.Span): assert args[0] == trace.Span() # Establish that the response is the type that we expect. + assert isinstance(response, trace.Span) assert response.name == "name_value" @@ -598,20 +699,36 @@ def test_create_span_from_dict(): test_create_span(request_type=dict) +def test_create_span_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = TraceServiceClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_span), "__call__") as call: + client.create_span() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == trace.Span() + + @pytest.mark.asyncio -async def test_create_span_async(transport: str = "grpc_asyncio"): +async def test_create_span_async( + transport: str = "grpc_asyncio", request_type=trace.Span +): client = TraceServiceAsyncClient( credentials=credentials.AnonymousCredentials(), transport=transport, ) # Everything is optional in proto3 as far as the runtime is concerned, # and we are mocking out the actual API, so just send an empty request. - request = trace.Span() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.create_span), "__call__" - ) as call: + with mock.patch.object(type(client.transport.create_span), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( trace.Span( @@ -628,7 +745,7 @@ async def test_create_span_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == trace.Span() # Establish that the response is the type that we expect. assert isinstance(response, trace.Span) @@ -642,6 +759,11 @@ async def test_create_span_async(transport: str = "grpc_asyncio"): assert response.span_kind == trace.Span.SpanKind.INTERNAL +@pytest.mark.asyncio +async def test_create_span_async_from_dict(): + await test_create_span_async(request_type=dict) + + def test_create_span_field_headers(): client = TraceServiceClient(credentials=credentials.AnonymousCredentials(),) @@ -651,7 +773,7 @@ def test_create_span_field_headers(): request.name = "name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.create_span), "__call__") as call: + with mock.patch.object(type(client.transport.create_span), "__call__") as call: call.return_value = trace.Span() client.create_span(request) @@ -676,9 +798,7 @@ async def test_create_span_field_headers_async(): request.name = "name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.create_span), "__call__" - ) as call: + with mock.patch.object(type(client.transport.create_span), "__call__") as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(trace.Span()) await client.create_span(request) @@ -729,7 +849,7 @@ def test_transport_instance(): credentials=credentials.AnonymousCredentials(), ) client = TraceServiceClient(transport=transport) - assert client._transport is transport + assert client.transport is transport def test_transport_get_channel(): @@ -747,10 +867,25 @@ def test_transport_get_channel(): assert channel +@pytest.mark.parametrize( + "transport_class", + [ + transports.TraceServiceGrpcTransport, + transports.TraceServiceGrpcAsyncIOTransport, + ], +) +def test_transport_adc(transport_class): + # Test default credentials are used if not provided. + with mock.patch.object(auth, "default") as adc: + adc.return_value = (credentials.AnonymousCredentials(), None) + transport_class() + adc.assert_called_once() + + def test_transport_grpc_default(): # A client should use the gRPC transport by default. client = TraceServiceClient(credentials=credentials.AnonymousCredentials(),) - assert isinstance(client._transport, transports.TraceServiceGrpcTransport,) + assert isinstance(client.transport, transports.TraceServiceGrpcTransport,) def test_trace_service_base_transport_error(): @@ -805,6 +940,17 @@ def test_trace_service_base_transport_with_credentials_file(): ) +def test_trace_service_base_transport_with_adc(): + # Test the default credentials are used if credentials and credentials_file are None. + with mock.patch.object(auth, "default") as adc, mock.patch( + "google.cloud.trace_v2.services.trace_service.transports.TraceServiceTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.TraceServiceTransport() + adc.assert_called_once() + + def test_trace_service_auth_adc(): # If no credentials are provided, we should use ADC credentials. with mock.patch.object(auth, "default") as adc: @@ -836,6 +982,51 @@ def test_trace_service_transport_auth_adc(): ) +@pytest.mark.parametrize( + "transport_class", + [transports.TraceServiceGrpcTransport, transports.TraceServiceGrpcAsyncIOTransport], +) +def test_trace_service_grpc_transport_client_cert_source_for_mtls(transport_class): + cred = credentials.AnonymousCredentials() + + # Check ssl_channel_credentials is used if provided. + with mock.patch.object(transport_class, "create_channel") as mock_create_channel: + mock_ssl_channel_creds = mock.Mock() + transport_class( + host="squid.clam.whelk", + credentials=cred, + ssl_channel_credentials=mock_ssl_channel_creds, + ) + mock_create_channel.assert_called_once_with( + "squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/trace.append", + ), + ssl_credentials=mock_ssl_channel_creds, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Check if ssl_channel_credentials is not provided, then client_cert_source_for_mtls + # is used. + with mock.patch.object(transport_class, "create_channel", return_value=mock.Mock()): + with mock.patch("grpc.ssl_channel_credentials") as mock_ssl_cred: + transport_class( + credentials=cred, + client_cert_source_for_mtls=client_cert_source_callback, + ) + expected_cert, expected_key = client_cert_source_callback() + mock_ssl_cred.assert_called_once_with( + certificate_chain=expected_cert, private_key=expected_key + ) + + def test_trace_service_host_no_port(): client = TraceServiceClient( credentials=credentials.AnonymousCredentials(), @@ -843,7 +1034,7 @@ def test_trace_service_host_no_port(): api_endpoint="cloudtrace.googleapis.com" ), ) - assert client._transport._host == "cloudtrace.googleapis.com:443" + assert client.transport._host == "cloudtrace.googleapis.com:443" def test_trace_service_host_with_port(): @@ -853,197 +1044,129 @@ def test_trace_service_host_with_port(): api_endpoint="cloudtrace.googleapis.com:8000" ), ) - assert client._transport._host == "cloudtrace.googleapis.com:8000" + assert client.transport._host == "cloudtrace.googleapis.com:8000" def test_trace_service_grpc_transport_channel(): - channel = grpc.insecure_channel("http://localhost/") + channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) - # Check that if channel is provided, mtls endpoint and client_cert_source - # won't be used. - callback = mock.MagicMock() + # Check that channel is used if provided. transport = transports.TraceServiceGrpcTransport( - host="squid.clam.whelk", - channel=channel, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=callback, + host="squid.clam.whelk", channel=channel, ) assert transport.grpc_channel == channel assert transport._host == "squid.clam.whelk:443" - assert not callback.called + assert transport._ssl_channel_credentials == None def test_trace_service_grpc_asyncio_transport_channel(): - channel = aio.insecure_channel("http://localhost/") + channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) - # Check that if channel is provided, mtls endpoint and client_cert_source - # won't be used. - callback = mock.MagicMock() + # Check that channel is used if provided. transport = transports.TraceServiceGrpcAsyncIOTransport( - host="squid.clam.whelk", - channel=channel, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=callback, + host="squid.clam.whelk", channel=channel, ) assert transport.grpc_channel == channel assert transport._host == "squid.clam.whelk:443" - assert not callback.called - - -@mock.patch("grpc.ssl_channel_credentials", autospec=True) -@mock.patch("google.api_core.grpc_helpers.create_channel", autospec=True) -def test_trace_service_grpc_transport_channel_mtls_with_client_cert_source( - grpc_create_channel, grpc_ssl_channel_cred -): - # Check that if channel is None, but api_mtls_endpoint and client_cert_source - # are provided, then a mTLS channel will be created. - mock_cred = mock.Mock() - - mock_ssl_cred = mock.Mock() - grpc_ssl_channel_cred.return_value = mock_ssl_cred - - mock_grpc_channel = mock.Mock() - grpc_create_channel.return_value = mock_grpc_channel - - transport = transports.TraceServiceGrpcTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=client_cert_source_callback, - ) - grpc_ssl_channel_cred.assert_called_once_with( - certificate_chain=b"cert bytes", private_key=b"key bytes" - ) - grpc_create_channel.assert_called_once_with( - "mtls.squid.clam.whelk:443", - credentials=mock_cred, - credentials_file=None, - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/trace.append", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel - - -@mock.patch("grpc.ssl_channel_credentials", autospec=True) -@mock.patch("google.api_core.grpc_helpers_async.create_channel", autospec=True) -def test_trace_service_grpc_asyncio_transport_channel_mtls_with_client_cert_source( - grpc_create_channel, grpc_ssl_channel_cred -): - # Check that if channel is None, but api_mtls_endpoint and client_cert_source - # are provided, then a mTLS channel will be created. - mock_cred = mock.Mock() - - mock_ssl_cred = mock.Mock() - grpc_ssl_channel_cred.return_value = mock_ssl_cred - - mock_grpc_channel = mock.Mock() - grpc_create_channel.return_value = mock_grpc_channel - - transport = transports.TraceServiceGrpcAsyncIOTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=client_cert_source_callback, - ) - grpc_ssl_channel_cred.assert_called_once_with( - certificate_chain=b"cert bytes", private_key=b"key bytes" - ) - grpc_create_channel.assert_called_once_with( - "mtls.squid.clam.whelk:443", - credentials=mock_cred, - credentials_file=None, - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/trace.append", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == None +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( - "api_mtls_endpoint", ["mtls.squid.clam.whelk", "mtls.squid.clam.whelk:443"] + "transport_class", + [transports.TraceServiceGrpcTransport, transports.TraceServiceGrpcAsyncIOTransport], ) -@mock.patch("google.api_core.grpc_helpers.create_channel", autospec=True) -def test_trace_service_grpc_transport_channel_mtls_with_adc( - grpc_create_channel, api_mtls_endpoint -): - # Check that if channel and client_cert_source are None, but api_mtls_endpoint - # is provided, then a mTLS channel will be created with SSL ADC. - mock_grpc_channel = mock.Mock() - grpc_create_channel.return_value = mock_grpc_channel - - # Mock google.auth.transport.grpc.SslCredentials class. - mock_ssl_cred = mock.Mock() - with mock.patch.multiple( - "google.auth.transport.grpc.SslCredentials", - __init__=mock.Mock(return_value=None), - ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), - ): - mock_cred = mock.Mock() - transport = transports.TraceServiceGrpcTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint=api_mtls_endpoint, - client_cert_source=None, - ) - grpc_create_channel.assert_called_once_with( - "mtls.squid.clam.whelk:443", - credentials=mock_cred, - credentials_file=None, - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/trace.append", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel +def test_trace_service_transport_channel_mtls_with_client_cert_source(transport_class): + with mock.patch( + "grpc.ssl_channel_credentials", autospec=True + ) as grpc_ssl_channel_cred: + with mock.patch.object( + transport_class, "create_channel" + ) as grpc_create_channel: + mock_ssl_cred = mock.Mock() + grpc_ssl_channel_cred.return_value = mock_ssl_cred + + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + + cred = credentials.AnonymousCredentials() + with pytest.warns(DeprecationWarning): + with mock.patch.object(auth, "default") as adc: + adc.return_value = (cred, None) + transport = transport_class( + host="squid.clam.whelk", + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=client_cert_source_callback, + ) + adc.assert_called_once() + + grpc_ssl_channel_cred.assert_called_once_with( + certificate_chain=b"cert bytes", private_key=b"key bytes" + ) + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/trace.append", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( - "api_mtls_endpoint", ["mtls.squid.clam.whelk", "mtls.squid.clam.whelk:443"] + "transport_class", + [transports.TraceServiceGrpcTransport, transports.TraceServiceGrpcAsyncIOTransport], ) -@mock.patch("google.api_core.grpc_helpers_async.create_channel", autospec=True) -def test_trace_service_grpc_asyncio_transport_channel_mtls_with_adc( - grpc_create_channel, api_mtls_endpoint -): - # Check that if channel and client_cert_source are None, but api_mtls_endpoint - # is provided, then a mTLS channel will be created with SSL ADC. - mock_grpc_channel = mock.Mock() - grpc_create_channel.return_value = mock_grpc_channel - - # Mock google.auth.transport.grpc.SslCredentials class. +def test_trace_service_transport_channel_mtls_with_adc(transport_class): mock_ssl_cred = mock.Mock() with mock.patch.multiple( "google.auth.transport.grpc.SslCredentials", __init__=mock.Mock(return_value=None), ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), ): - mock_cred = mock.Mock() - transport = transports.TraceServiceGrpcAsyncIOTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint=api_mtls_endpoint, - client_cert_source=None, - ) - grpc_create_channel.assert_called_once_with( - "mtls.squid.clam.whelk:443", - credentials=mock_cred, - credentials_file=None, - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/trace.append", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel + with mock.patch.object( + transport_class, "create_channel" + ) as grpc_create_channel: + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + mock_cred = mock.Mock() + + with pytest.warns(DeprecationWarning): + transport = transport_class( + host="squid.clam.whelk", + credentials=mock_cred, + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=None, + ) + + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=mock_cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/trace.append", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + assert transport.grpc_channel == mock_grpc_channel def test_span_path(): @@ -1071,6 +1194,107 @@ def test_parse_span_path(): assert expected == actual +def test_common_billing_account_path(): + billing_account = "cuttlefish" + + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = TraceServiceClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "mussel", + } + path = TraceServiceClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = TraceServiceClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "winkle" + + expected = "folders/{folder}".format(folder=folder,) + actual = TraceServiceClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "nautilus", + } + path = TraceServiceClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = TraceServiceClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "scallop" + + expected = "organizations/{organization}".format(organization=organization,) + actual = TraceServiceClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "abalone", + } + path = TraceServiceClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = TraceServiceClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "squid" + + expected = "projects/{project}".format(project=project,) + actual = TraceServiceClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "clam", + } + path = TraceServiceClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = TraceServiceClient.parse_common_project_path(path) + assert expected == actual + + +def test_common_location_path(): + project = "whelk" + location = "octopus" + + expected = "projects/{project}/locations/{location}".format( + project=project, location=location, + ) + actual = TraceServiceClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "oyster", + "location": "nudibranch", + } + path = TraceServiceClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = TraceServiceClient.parse_common_location_path(path) + assert expected == actual + + def test_client_withDEFAULT_CLIENT_INFO(): client_info = gapic_v1.client_info.ClientInfo() From d814c6f30d87553512cb7587bd9b957a2fb85150 Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Fri, 2 Apr 2021 22:57:19 +0000 Subject: [PATCH 47/47] fix: keep attribute --- google/cloud/trace_v2/types/trace.py | 8 ++++---- synth.metadata | 2 +- synth.py | 7 +++++++ 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/google/cloud/trace_v2/types/trace.py b/google/cloud/trace_v2/types/trace.py index 5a3b1497..903d298c 100644 --- a/google/cloud/trace_v2/types/trace.py +++ b/google/cloud/trace_v2/types/trace.py @@ -194,7 +194,7 @@ class MessageEvent(proto.Message): r"""An event describing a message sent/received between Spans. Attributes: - type_ (google.cloud.trace_v2.types.Span.TimeEvent.MessageEvent.Type): + type (google.cloud.trace_v2.types.Span.TimeEvent.MessageEvent.Type): Type of MessageEvent. Indicates whether the message was sent or received. id (int): @@ -217,7 +217,7 @@ class Type(proto.Enum): SENT = 1 RECEIVED = 2 - type_ = proto.Field( + type = proto.Field( proto.ENUM, number=1, enum="Span.TimeEvent.MessageEvent.Type", ) @@ -278,7 +278,7 @@ class Link(proto.Message): The [TRACE_ID] for a trace within a project. span_id (str): The [SPAN_ID] for a span within a trace. - type_ (google.cloud.trace_v2.types.Span.Link.Type): + type (google.cloud.trace_v2.types.Span.Link.Type): The relationship of the current span relative to the linked span. attributes (google.cloud.trace_v2.types.Span.Attributes): @@ -298,7 +298,7 @@ class Type(proto.Enum): span_id = proto.Field(proto.STRING, number=2) - type_ = proto.Field(proto.ENUM, number=3, enum="Span.Link.Type",) + type = proto.Field(proto.ENUM, number=3, enum="Span.Link.Type",) attributes = proto.Field(proto.MESSAGE, number=4, message="Span.Attributes",) diff --git a/synth.metadata b/synth.metadata index 549a7fa5..ef817e01 100644 --- a/synth.metadata +++ b/synth.metadata @@ -4,7 +4,7 @@ "git": { "name": ".", "remote": "git@github.com:googleapis/python-trace", - "sha": "8685deeca16bd59a8f3baadd8bbc964ddb0248c8" + "sha": "1806034ea0beded6473de7d0938b92243d653c4f" } }, { diff --git a/synth.py b/synth.py index 4d00182e..87ac3b93 100644 --- a/synth.py +++ b/synth.py @@ -36,6 +36,13 @@ s.move(library, excludes=["docs/index.rst", "setup.py"]) +# Rename field `type_` to `type` in v1 and v2 to avoid breaking change +s.replace( + "google/**/types/*.py", + "type_", + "type" +) + # ---------------------------------------------------------------------------- # Add templated files # ----------------------------------------------------------------------------