chore: preserve 'pytype' artifacts during synth (#90)

* chore: use '.pytype' vs. 'pytype_output' for pytype cache

* chore: add snippet-bot to CI (via synth)

* chore: drop obsolete warning about editable installs

* chore: add secrets manager, docs-presubmit to CI (via synth)

* chore: manual synth

Closes: #66.
Closes: #56.
diff --git a/.github/snippet-bot.yml b/.github/snippet-bot.yml
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/.github/snippet-bot.yml
diff --git a/.gitignore b/.gitignore
index 157bfb3..8e08ceb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,7 +29,7 @@
 .nox
 .cache
 .pytest_cache
-pytype_output
+.pytype
 
 
 # Mac
@@ -47,6 +47,7 @@
 # Built documentation
 docs/_build
 bigquery/docs/generated
+docs.metadata
 
 # Virtual environment
 env/
diff --git a/.kokoro/build.sh b/.kokoro/build.sh
index 5390f03..adc4be4 100755
--- a/.kokoro/build.sh
+++ b/.kokoro/build.sh
@@ -36,4 +36,10 @@
 python3.6 -m pip install --upgrade --quiet nox
 python3.6 -m nox --version
 
-python3.6 -m nox
+# 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:-}"
+else
+    python3.6 -m nox
+fi
diff --git a/.kokoro/docker/docs/Dockerfile b/.kokoro/docker/docs/Dockerfile
new file mode 100644
index 0000000..412b0b5
--- /dev/null
+++ b/.kokoro/docker/docs/Dockerfile
@@ -0,0 +1,98 @@
+# 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 ubuntu:20.04
+
+ENV DEBIAN_FRONTEND noninteractive
+
+# Ensure local Python is preferred over distribution Python.
+ENV PATH /usr/local/bin:$PATH
+
+# Install dependencies.
+RUN apt-get update \
+  && apt-get install -y --no-install-recommends \
+    apt-transport-https \
+    build-essential \
+    ca-certificates \
+    curl \
+    dirmngr \
+    git \
+    gpg-agent \
+    graphviz \
+    libbz2-dev \
+    libdb5.3-dev \
+    libexpat1-dev \
+    libffi-dev \
+    liblzma-dev \
+    libreadline-dev \
+    libsnappy-dev \
+    libssl-dev \
+    libsqlite3-dev \
+    portaudio19-dev \
+    redis-server \
+    software-properties-common \
+    ssh \
+    sudo \
+    tcl \
+    tcl-dev \
+    tk \
+    tk-dev \
+    uuid-dev \
+    wget \
+    zlib1g-dev \
+  && add-apt-repository universe \
+  && apt-get update \
+  && apt-get -y install jq \
+  && apt-get clean autoclean \
+  && apt-get autoremove -y \
+  && rm -rf /var/lib/apt/lists/* \
+  && rm -f /var/cache/apt/archives/*.deb
+
+
+COPY fetch_gpg_keys.sh /tmp
+# Install the desired versions of Python.
+RUN set -ex \
+    && export GNUPGHOME="$(mktemp -d)" \
+    && echo "disable-ipv6" >> "${GNUPGHOME}/dirmngr.conf" \
+    && /tmp/fetch_gpg_keys.sh \
+    && for PYTHON_VERSION in 3.7.8 3.8.5; do \
+        wget --no-check-certificate -O python-${PYTHON_VERSION}.tar.xz "https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz" \
+        && wget --no-check-certificate -O python-${PYTHON_VERSION}.tar.xz.asc "https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz.asc" \
+        && gpg --batch --verify python-${PYTHON_VERSION}.tar.xz.asc python-${PYTHON_VERSION}.tar.xz \
+        && rm -r python-${PYTHON_VERSION}.tar.xz.asc \
+        && mkdir -p /usr/src/python-${PYTHON_VERSION} \
+        && tar -xJC /usr/src/python-${PYTHON_VERSION} --strip-components=1 -f python-${PYTHON_VERSION}.tar.xz \
+        && rm python-${PYTHON_VERSION}.tar.xz \
+        && cd /usr/src/python-${PYTHON_VERSION} \
+        && ./configure \
+            --enable-shared \
+            # This works only on Python 2.7 and throws a warning on every other
+            # version, but seems otherwise harmless.
+            --enable-unicode=ucs4 \
+            --with-system-ffi \
+            --without-ensurepip \
+        && make -j$(nproc) \
+        && make install \
+        && ldconfig \
+  ; done \
+  && rm -rf "${GNUPGHOME}" \
+  && rm -rf /usr/src/python* \
+  && rm -rf ~/.cache/
+
+RUN wget -O /tmp/get-pip.py 'https://bootstrap.pypa.io/get-pip.py' \
+  && python3.7 /tmp/get-pip.py \
+  && python3.8 /tmp/get-pip.py \
+  && rm /tmp/get-pip.py
+
+CMD ["python3.7"]
diff --git a/.kokoro/docker/docs/fetch_gpg_keys.sh b/.kokoro/docker/docs/fetch_gpg_keys.sh
new file mode 100755
index 0000000..d653dd8
--- /dev/null
+++ b/.kokoro/docker/docs/fetch_gpg_keys.sh
@@ -0,0 +1,45 @@
+#!/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.
+
+# A script to fetch gpg keys with retry.
+# Avoid jinja parsing the file.
+# 
+
+function retry {
+    if [[ "${#}" -le 1 ]]; then
+	echo "Usage: ${0} retry_count commands.."
+	exit 1
+    fi
+    local retries=${1}
+    local command="${@:2}"
+    until [[ "${retries}" -le 0 ]]; do
+	$command && return 0
+	if [[ $? -ne 0 ]]; then
+	    echo "command failed, retrying"
+	    ((retries--))
+	fi
+    done
+    return 1
+}
+
+# 3.6.9, 3.7.5 (Ned Deily)
+retry 3 gpg --keyserver ha.pool.sks-keyservers.net --recv-keys \
+      0D96DF4D4110E5C43FBFB17F2D347EA6AA65421D
+
+# 3.8.0 (Ɓukasz Langa)
+retry 3 gpg --keyserver ha.pool.sks-keyservers.net --recv-keys \
+      E3FF2839C048B25C084DEBE9B26995E310250568
+
+# 
diff --git a/.kokoro/docs/common.cfg b/.kokoro/docs/common.cfg
index 3a5cabd..1aca89f 100644
--- a/.kokoro/docs/common.cfg
+++ b/.kokoro/docs/common.cfg
@@ -11,12 +11,12 @@
 gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline"
 
 # Use the trampoline script to run in docker.
-build_file: "python-api-core/.kokoro/trampoline.sh"
+build_file: "python-api-core/.kokoro/trampoline_v2.sh"
 
 # Configure the docker image for kokoro-trampoline.
 env_vars: {
     key: "TRAMPOLINE_IMAGE"
-    value: "gcr.io/cloud-devrel-kokoro-resources/python-multi"
+    value: "gcr.io/cloud-devrel-kokoro-resources/python-lib-docs"
 }
 env_vars: {
     key: "TRAMPOLINE_BUILD_FILE"
@@ -28,6 +28,23 @@
     value: "docs-staging"
 }
 
+env_vars: {
+    key: "V2_STAGING_BUCKET"
+    value: "docs-staging-v2-staging"
+}
+
+# It will upload the docker image after successful builds.
+env_vars: {
+    key: "TRAMPOLINE_IMAGE_UPLOAD"
+    value: "true"
+}
+
+# It will always build the docker image.
+env_vars: {
+    key: "TRAMPOLINE_DOCKERFILE"
+    value: ".kokoro/docker/docs/Dockerfile"
+}
+
 # Fetch the token needed for reporting release status to GitHub
 before_action {
   fetch_keystore {
diff --git a/.kokoro/docs/docs-presubmit.cfg b/.kokoro/docs/docs-presubmit.cfg
new file mode 100644
index 0000000..1118107
--- /dev/null
+++ b/.kokoro/docs/docs-presubmit.cfg
@@ -0,0 +1,17 @@
+# Format: //devtools/kokoro/config/proto/build.proto
+
+env_vars: {
+    key: "STAGING_BUCKET"
+    value: "gcloud-python-test"
+}
+
+env_vars: {
+    key: "V2_STAGING_BUCKET"
+    value: "gcloud-python-test"
+}
+
+# We only upload the image in the main `docs` build.
+env_vars: {
+    key: "TRAMPOLINE_IMAGE_UPLOAD"
+    value: "false"
+}
diff --git a/.kokoro/populate-secrets.sh b/.kokoro/populate-secrets.sh
new file mode 100755
index 0000000..f525142
--- /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/publish-docs.sh b/.kokoro/publish-docs.sh
index db50ceb..8acb14e 100755
--- a/.kokoro/publish-docs.sh
+++ b/.kokoro/publish-docs.sh
@@ -18,26 +18,16 @@
 # Disable buffering, so that the logs stream through.
 export PYTHONUNBUFFERED=1
 
-cd github/python-api-core
-
-# Remove old nox
-python3.6 -m pip uninstall --yes --quiet nox-automation
+export PATH="${HOME}/.local/bin:${PATH}"
 
 # Install nox
-python3.6 -m pip install --upgrade --quiet nox
-python3.6 -m nox --version
+python3 -m pip install --user --upgrade --quiet nox
+python3 -m nox --version
 
 # build docs
 nox -s docs
 
-python3 -m pip install gcp-docuploader
-
-# install a json parser
-sudo apt-get update
-sudo apt-get -y install software-properties-common
-sudo add-apt-repository universe
-sudo apt-get update
-sudo apt-get -y install jq
+python3 -m pip install --user gcp-docuploader
 
 # create metadata
 python3 -m docuploader create-metadata \
@@ -52,4 +42,23 @@
 cat docs.metadata
 
 # upload docs
-python3 -m docuploader upload docs/_build/html --metadata-file docs.metadata --staging-bucket docs-staging
+python3 -m docuploader upload docs/_build/html --metadata-file docs.metadata --staging-bucket "${STAGING_BUCKET}"
+
+
+# docfx yaml files
+nox -s docfx
+
+# create metadata.
+python3 -m docuploader create-metadata \
+  --name=$(jq --raw-output '.name // empty' .repo-metadata.json) \
+  --version=$(python3 setup.py --version) \
+  --language=$(jq --raw-output '.language // empty' .repo-metadata.json) \
+  --distribution-name=$(python3 setup.py --name) \
+  --product-page=$(jq --raw-output '.product_documentation // empty' .repo-metadata.json) \
+  --github-repository=$(jq --raw-output '.repo // empty' .repo-metadata.json) \
+  --issue-tracker=$(jq --raw-output '.issue_tracker // empty' .repo-metadata.json)
+
+cat docs.metadata
+
+# upload docs
+python3 -m docuploader upload docs/_build/html/docfx_yaml --metadata-file docs.metadata --destination-prefix docfx --staging-bucket "${V2_STAGING_BUCKET}"
diff --git a/.kokoro/release/common.cfg b/.kokoro/release/common.cfg
index dbe45a6..4a949c6 100644
--- a/.kokoro/release/common.cfg
+++ b/.kokoro/release/common.cfg
@@ -23,42 +23,18 @@
     value: "github/python-api-core/.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 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 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/.kokoro/samples/python3.6/common.cfg b/.kokoro/samples/python3.6/common.cfg
index 0a79070..7b4f5cd 100644
--- a/.kokoro/samples/python3.6/common.cfg
+++ b/.kokoro/samples/python3.6/common.cfg
@@ -13,6 +13,12 @@
     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-api-core/.kokoro/test-samples.sh"
diff --git a/.kokoro/samples/python3.7/common.cfg b/.kokoro/samples/python3.7/common.cfg
index 6eaddc3..1198d7b 100644
--- a/.kokoro/samples/python3.7/common.cfg
+++ b/.kokoro/samples/python3.7/common.cfg
@@ -13,6 +13,12 @@
     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-api-core/.kokoro/test-samples.sh"
diff --git a/.kokoro/samples/python3.8/common.cfg b/.kokoro/samples/python3.8/common.cfg
index 461431a..b7ec7f5 100644
--- a/.kokoro/samples/python3.8/common.cfg
+++ b/.kokoro/samples/python3.8/common.cfg
@@ -13,6 +13,12 @@
     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-api-core/.kokoro/test-samples.sh"
diff --git a/.kokoro/trampoline.sh b/.kokoro/trampoline.sh
index e8c4251..f39236e 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/.kokoro/trampoline_v2.sh b/.kokoro/trampoline_v2.sh
new file mode 100755
index 0000000..719bcd5
--- /dev/null
+++ b/.kokoro/trampoline_v2.sh
@@ -0,0 +1,487 @@
+#!/usr/bin/env 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.
+
+# trampoline_v2.sh
+#
+# This script does 3 things.
+#
+# 1. Prepare the Docker image for the test
+# 2. Run the Docker with appropriate flags to run the test
+# 3. Upload the newly built Docker image
+#
+# in a way that is somewhat compatible with trampoline_v1.
+#
+# To run this script, first download few files from gcs to /dev/shm.
+# (/dev/shm is passed into the container as KOKORO_GFILE_DIR).
+#
+# gsutil cp gs://cloud-devrel-kokoro-resources/python-docs-samples/secrets_viewer_service_account.json /dev/shm
+# gsutil cp gs://cloud-devrel-kokoro-resources/python-docs-samples/automl_secrets.txt /dev/shm
+#
+# Then run the script.
+# .kokoro/trampoline_v2.sh
+#
+# These environment variables are required:
+# TRAMPOLINE_IMAGE: The docker image to use.
+# TRAMPOLINE_DOCKERFILE: The location of the Dockerfile.
+#
+# You can optionally change these environment variables:
+# TRAMPOLINE_IMAGE_UPLOAD:
+#     (true|false): Whether to upload the Docker image after the
+#                   successful builds.
+# TRAMPOLINE_BUILD_FILE: The script to run in the docker container.
+# TRAMPOLINE_WORKSPACE: The workspace path in the docker container.
+#                       Defaults to /workspace.
+# Potentially there are some repo specific envvars in .trampolinerc in
+# the project root.
+
+
+set -euo pipefail
+
+TRAMPOLINE_VERSION="2.0.5"
+
+if command -v tput >/dev/null && [[ -n "${TERM:-}" ]]; then
+  readonly IO_COLOR_RED="$(tput setaf 1)"
+  readonly IO_COLOR_GREEN="$(tput setaf 2)"
+  readonly IO_COLOR_YELLOW="$(tput setaf 3)"
+  readonly IO_COLOR_RESET="$(tput sgr0)"
+else
+  readonly IO_COLOR_RED=""
+  readonly IO_COLOR_GREEN=""
+  readonly IO_COLOR_YELLOW=""
+  readonly IO_COLOR_RESET=""
+fi
+
+function function_exists {
+    [ $(LC_ALL=C type -t $1)"" == "function" ]
+}
+
+# Logs a message using the given color. The first argument must be one
+# of the IO_COLOR_* variables defined above, such as
+# "${IO_COLOR_YELLOW}". The remaining arguments will be logged in the
+# given color. The log message will also have an RFC-3339 timestamp
+# prepended (in UTC). You can disable the color output by setting
+# TERM=vt100.
+function log_impl() {
+    local color="$1"
+    shift
+    local timestamp="$(date -u "+%Y-%m-%dT%H:%M:%SZ")"
+    echo "================================================================"
+    echo "${color}${timestamp}:" "$@" "${IO_COLOR_RESET}"
+    echo "================================================================"
+}
+
+# Logs the given message with normal coloring and a timestamp.
+function log() {
+  log_impl "${IO_COLOR_RESET}" "$@"
+}
+
+# Logs the given message in green with a timestamp.
+function log_green() {
+  log_impl "${IO_COLOR_GREEN}" "$@"
+}
+
+# Logs the given message in yellow with a timestamp.
+function log_yellow() {
+  log_impl "${IO_COLOR_YELLOW}" "$@"
+}
+
+# Logs the given message in red with a timestamp.
+function log_red() {
+  log_impl "${IO_COLOR_RED}" "$@"
+}
+
+readonly tmpdir=$(mktemp -d -t ci-XXXXXXXX)
+readonly tmphome="${tmpdir}/h"
+mkdir -p "${tmphome}"
+
+function cleanup() {
+    rm -rf "${tmpdir}"
+}
+trap cleanup EXIT
+
+RUNNING_IN_CI="${RUNNING_IN_CI:-false}"
+
+# The workspace in the container, defaults to /workspace.
+TRAMPOLINE_WORKSPACE="${TRAMPOLINE_WORKSPACE:-/workspace}"
+
+pass_down_envvars=(
+    # TRAMPOLINE_V2 variables.
+    # Tells scripts whether they are running as part of CI or not.
+    "RUNNING_IN_CI"
+    # Indicates which CI system we're in.
+    "TRAMPOLINE_CI"
+    # Indicates the version of the script.
+    "TRAMPOLINE_VERSION"
+)
+
+log_yellow "Building with Trampoline ${TRAMPOLINE_VERSION}"
+
+# Detect which CI systems we're in. If we're in any of the CI systems
+# we support, `RUNNING_IN_CI` will be true and `TRAMPOLINE_CI` will be
+# the name of the CI system. Both envvars will be passing down to the
+# container for telling which CI system we're in.
+if [[ -n "${KOKORO_BUILD_ID:-}" ]]; then
+    # descriptive env var for indicating it's on CI.
+    RUNNING_IN_CI="true"
+    TRAMPOLINE_CI="kokoro"
+    if [[ "${TRAMPOLINE_USE_LEGACY_SERVICE_ACCOUNT:-}" == "true" ]]; then
+	if [[ ! -f "${KOKORO_GFILE_DIR}/kokoro-trampoline.service-account.json" ]]; then
+	    log_red "${KOKORO_GFILE_DIR}/kokoro-trampoline.service-account.json does not exist. Did you forget to mount cloud-devrel-kokoro-resources/trampoline? Aborting."
+	    exit 1
+	fi
+	# This service account will be activated later.
+	TRAMPOLINE_SERVICE_ACCOUNT="${KOKORO_GFILE_DIR}/kokoro-trampoline.service-account.json"
+    else
+	if [[ "${TRAMPOLINE_VERBOSE:-}" == "true" ]]; then
+	    gcloud auth list
+	fi
+	log_yellow "Configuring Container Registry access"
+	gcloud auth configure-docker --quiet
+    fi
+    pass_down_envvars+=(
+	# KOKORO dynamic variables.
+	"KOKORO_BUILD_NUMBER"
+	"KOKORO_BUILD_ID"
+	"KOKORO_JOB_NAME"
+	"KOKORO_GIT_COMMIT"
+	"KOKORO_GITHUB_COMMIT"
+	"KOKORO_GITHUB_PULL_REQUEST_NUMBER"
+	"KOKORO_GITHUB_PULL_REQUEST_COMMIT"
+	# For Build Cop Bot
+	"KOKORO_GITHUB_COMMIT_URL"
+	"KOKORO_GITHUB_PULL_REQUEST_URL"
+    )
+elif [[ "${TRAVIS:-}" == "true" ]]; then
+    RUNNING_IN_CI="true"
+    TRAMPOLINE_CI="travis"
+    pass_down_envvars+=(
+	"TRAVIS_BRANCH"
+	"TRAVIS_BUILD_ID"
+	"TRAVIS_BUILD_NUMBER"
+	"TRAVIS_BUILD_WEB_URL"
+	"TRAVIS_COMMIT"
+	"TRAVIS_COMMIT_MESSAGE"
+	"TRAVIS_COMMIT_RANGE"
+	"TRAVIS_JOB_NAME"
+	"TRAVIS_JOB_NUMBER"
+	"TRAVIS_JOB_WEB_URL"
+	"TRAVIS_PULL_REQUEST"
+	"TRAVIS_PULL_REQUEST_BRANCH"
+	"TRAVIS_PULL_REQUEST_SHA"
+	"TRAVIS_PULL_REQUEST_SLUG"
+	"TRAVIS_REPO_SLUG"
+	"TRAVIS_SECURE_ENV_VARS"
+	"TRAVIS_TAG"
+    )
+elif [[ -n "${GITHUB_RUN_ID:-}" ]]; then
+    RUNNING_IN_CI="true"
+    TRAMPOLINE_CI="github-workflow"
+    pass_down_envvars+=(
+	"GITHUB_WORKFLOW"
+	"GITHUB_RUN_ID"
+	"GITHUB_RUN_NUMBER"
+	"GITHUB_ACTION"
+	"GITHUB_ACTIONS"
+	"GITHUB_ACTOR"
+	"GITHUB_REPOSITORY"
+	"GITHUB_EVENT_NAME"
+	"GITHUB_EVENT_PATH"
+	"GITHUB_SHA"
+	"GITHUB_REF"
+	"GITHUB_HEAD_REF"
+	"GITHUB_BASE_REF"
+    )
+elif [[ "${CIRCLECI:-}" == "true" ]]; then
+    RUNNING_IN_CI="true"
+    TRAMPOLINE_CI="circleci"
+    pass_down_envvars+=(
+	"CIRCLE_BRANCH"
+	"CIRCLE_BUILD_NUM"
+	"CIRCLE_BUILD_URL"
+	"CIRCLE_COMPARE_URL"
+	"CIRCLE_JOB"
+	"CIRCLE_NODE_INDEX"
+	"CIRCLE_NODE_TOTAL"
+	"CIRCLE_PREVIOUS_BUILD_NUM"
+	"CIRCLE_PROJECT_REPONAME"
+	"CIRCLE_PROJECT_USERNAME"
+	"CIRCLE_REPOSITORY_URL"
+	"CIRCLE_SHA1"
+	"CIRCLE_STAGE"
+	"CIRCLE_USERNAME"
+	"CIRCLE_WORKFLOW_ID"
+	"CIRCLE_WORKFLOW_JOB_ID"
+	"CIRCLE_WORKFLOW_UPSTREAM_JOB_IDS"
+	"CIRCLE_WORKFLOW_WORKSPACE_ID"
+    )
+fi
+
+# Configure the service account for pulling the docker image.
+function repo_root() {
+    local dir="$1"
+    while [[ ! -d "${dir}/.git" ]]; do
+	dir="$(dirname "$dir")"
+    done
+    echo "${dir}"
+}
+
+# Detect the project root. In CI builds, we assume the script is in
+# the git tree and traverse from there, otherwise, traverse from `pwd`
+# to find `.git` directory.
+if [[ "${RUNNING_IN_CI:-}" == "true" ]]; then
+    PROGRAM_PATH="$(realpath "$0")"
+    PROGRAM_DIR="$(dirname "${PROGRAM_PATH}")"
+    PROJECT_ROOT="$(repo_root "${PROGRAM_DIR}")"
+else
+    PROJECT_ROOT="$(repo_root $(pwd))"
+fi
+
+log_yellow "Changing to the project root: ${PROJECT_ROOT}."
+cd "${PROJECT_ROOT}"
+
+# To support relative path for `TRAMPOLINE_SERVICE_ACCOUNT`, we need
+# to use this environment variable in `PROJECT_ROOT`.
+if [[ -n "${TRAMPOLINE_SERVICE_ACCOUNT:-}" ]]; then
+
+    mkdir -p "${tmpdir}/gcloud"
+    gcloud_config_dir="${tmpdir}/gcloud"
+
+    log_yellow "Using isolated gcloud config: ${gcloud_config_dir}."
+    export CLOUDSDK_CONFIG="${gcloud_config_dir}"
+
+    log_yellow "Using ${TRAMPOLINE_SERVICE_ACCOUNT} for authentication."
+    gcloud auth activate-service-account \
+	   --key-file "${TRAMPOLINE_SERVICE_ACCOUNT}"
+    log_yellow "Configuring Container Registry access"
+    gcloud auth configure-docker --quiet
+fi
+
+required_envvars=(
+    # The basic trampoline configurations.
+    "TRAMPOLINE_IMAGE"
+    "TRAMPOLINE_BUILD_FILE"
+)
+
+if [[ -f "${PROJECT_ROOT}/.trampolinerc" ]]; then
+    source "${PROJECT_ROOT}/.trampolinerc"
+fi
+
+log_yellow "Checking environment variables."
+for e in "${required_envvars[@]}"
+do
+    if [[ -z "${!e:-}" ]]; then
+	log "Missing ${e} env var. Aborting."
+	exit 1
+    fi
+done
+
+# We want to support legacy style TRAMPOLINE_BUILD_FILE used with V1
+# script: e.g. "github/repo-name/.kokoro/run_tests.sh"
+TRAMPOLINE_BUILD_FILE="${TRAMPOLINE_BUILD_FILE#github/*/}"
+log_yellow "Using TRAMPOLINE_BUILD_FILE: ${TRAMPOLINE_BUILD_FILE}"
+
+# ignore error on docker operations and test execution
+set +e
+
+log_yellow "Preparing Docker image."
+# We only download the docker image in CI builds.
+if [[ "${RUNNING_IN_CI:-}" == "true" ]]; then
+    # Download the docker image specified by `TRAMPOLINE_IMAGE`
+
+    # We may want to add --max-concurrent-downloads flag.
+
+    log_yellow "Start pulling the Docker image: ${TRAMPOLINE_IMAGE}."
+    if docker pull "${TRAMPOLINE_IMAGE}"; then
+	log_green "Finished pulling the Docker image: ${TRAMPOLINE_IMAGE}."
+	has_image="true"
+    else
+	log_red "Failed pulling the Docker image: ${TRAMPOLINE_IMAGE}."
+	has_image="false"
+    fi
+else
+    # For local run, check if we have the image.
+    if docker images "${TRAMPOLINE_IMAGE}:latest" | grep "${TRAMPOLINE_IMAGE}"; then
+	has_image="true"
+    else
+	has_image="false"
+    fi
+fi
+
+
+# The default user for a Docker container has uid 0 (root). To avoid
+# creating root-owned files in the build directory we tell docker to
+# use the current user ID.
+user_uid="$(id -u)"
+user_gid="$(id -g)"
+user_name="$(id -un)"
+
+# To allow docker in docker, we add the user to the docker group in
+# the host os.
+docker_gid=$(cut -d: -f3 < <(getent group docker))
+
+update_cache="false"
+if [[ "${TRAMPOLINE_DOCKERFILE:-none}" != "none" ]]; then
+    # Build the Docker image from the source.
+    context_dir=$(dirname "${TRAMPOLINE_DOCKERFILE}")
+    docker_build_flags=(
+	"-f" "${TRAMPOLINE_DOCKERFILE}"
+	"-t" "${TRAMPOLINE_IMAGE}"
+	"--build-arg" "UID=${user_uid}"
+	"--build-arg" "USERNAME=${user_name}"
+    )
+    if [[ "${has_image}" == "true" ]]; then
+	docker_build_flags+=("--cache-from" "${TRAMPOLINE_IMAGE}")
+    fi
+
+    log_yellow "Start building the docker image."
+    if [[ "${TRAMPOLINE_VERBOSE:-false}" == "true" ]]; then
+	echo "docker build" "${docker_build_flags[@]}" "${context_dir}"
+    fi
+
+    # ON CI systems, we want to suppress docker build logs, only
+    # output the logs when it fails.
+    if [[ "${RUNNING_IN_CI:-}" == "true" ]]; then
+	if docker build "${docker_build_flags[@]}" "${context_dir}" \
+		  > "${tmpdir}/docker_build.log" 2>&1; then
+	    if [[ "${TRAMPOLINE_VERBOSE:-}" == "true" ]]; then
+		cat "${tmpdir}/docker_build.log"
+	    fi
+
+	    log_green "Finished building the docker image."
+	    update_cache="true"
+	else
+	    log_red "Failed to build the Docker image, aborting."
+	    log_yellow "Dumping the build logs:"
+	    cat "${tmpdir}/docker_build.log"
+	    exit 1
+	fi
+    else
+	if docker build "${docker_build_flags[@]}" "${context_dir}"; then
+	    log_green "Finished building the docker image."
+	    update_cache="true"
+	else
+	    log_red "Failed to build the Docker image, aborting."
+	    exit 1
+	fi
+    fi
+else
+    if [[ "${has_image}" != "true" ]]; then
+	log_red "We do not have ${TRAMPOLINE_IMAGE} locally, aborting."
+	exit 1
+    fi
+fi
+
+# We use an array for the flags so they are easier to document.
+docker_flags=(
+    # Remove the container after it exists.
+    "--rm"
+
+    # Use the host network.
+    "--network=host"
+
+    # Run in priviledged mode. We are not using docker for sandboxing or
+    # isolation, just for packaging our dev tools.
+    "--privileged"
+
+    # Run the docker script with the user id. Because the docker image gets to
+    # write in ${PWD} you typically want this to be your user id.
+    # To allow docker in docker, we need to use docker gid on the host.
+    "--user" "${user_uid}:${docker_gid}"
+
+    # Pass down the USER.
+    "--env" "USER=${user_name}"
+
+    # Mount the project directory inside the Docker container.
+    "--volume" "${PROJECT_ROOT}:${TRAMPOLINE_WORKSPACE}"
+    "--workdir" "${TRAMPOLINE_WORKSPACE}"
+    "--env" "PROJECT_ROOT=${TRAMPOLINE_WORKSPACE}"
+
+    # Mount the temporary home directory.
+    "--volume" "${tmphome}:/h"
+    "--env" "HOME=/h"
+
+    # Allow docker in docker.
+    "--volume" "/var/run/docker.sock:/var/run/docker.sock"
+
+    # Mount the /tmp so that docker in docker can mount the files
+    # there correctly.
+    "--volume" "/tmp:/tmp"
+    # Pass down the KOKORO_GFILE_DIR and KOKORO_KEYSTORE_DIR
+    # TODO(tmatsuo): This part is not portable.
+    "--env" "TRAMPOLINE_SECRET_DIR=/secrets"
+    "--volume" "${KOKORO_GFILE_DIR:-/dev/shm}:/secrets/gfile"
+    "--env" "KOKORO_GFILE_DIR=/secrets/gfile"
+    "--volume" "${KOKORO_KEYSTORE_DIR:-/dev/shm}:/secrets/keystore"
+    "--env" "KOKORO_KEYSTORE_DIR=/secrets/keystore"
+)
+
+# Add an option for nicer output if the build gets a tty.
+if [[ -t 0 ]]; then
+    docker_flags+=("-it")
+fi
+
+# Passing down env vars
+for e in "${pass_down_envvars[@]}"
+do
+    if [[ -n "${!e:-}" ]]; then
+	docker_flags+=("--env" "${e}=${!e}")
+    fi
+done
+
+# If arguments are given, all arguments will become the commands run
+# in the container, otherwise run TRAMPOLINE_BUILD_FILE.
+if [[ $# -ge 1 ]]; then
+    log_yellow "Running the given commands '" "${@:1}" "' in the container."
+    readonly commands=("${@:1}")
+    if [[ "${TRAMPOLINE_VERBOSE:-}" == "true" ]]; then
+	echo docker run "${docker_flags[@]}" "${TRAMPOLINE_IMAGE}" "${commands[@]}"
+    fi
+    docker run "${docker_flags[@]}" "${TRAMPOLINE_IMAGE}" "${commands[@]}"
+else
+    log_yellow "Running the tests in a Docker container."
+    docker_flags+=("--entrypoint=${TRAMPOLINE_BUILD_FILE}")
+    if [[ "${TRAMPOLINE_VERBOSE:-}" == "true" ]]; then
+	echo docker run "${docker_flags[@]}" "${TRAMPOLINE_IMAGE}"
+    fi
+    docker run "${docker_flags[@]}" "${TRAMPOLINE_IMAGE}"
+fi
+
+
+test_retval=$?
+
+if [[ ${test_retval} -eq 0 ]]; then
+    log_green "Build finished with ${test_retval}"
+else
+    log_red "Build finished with ${test_retval}"
+fi
+
+# Only upload it when the test passes.
+if [[ "${update_cache}" == "true" ]] && \
+       [[ $test_retval == 0 ]] && \
+       [[ "${TRAMPOLINE_IMAGE_UPLOAD:-false}" == "true" ]]; then
+    log_yellow "Uploading the Docker image."
+    if docker push "${TRAMPOLINE_IMAGE}"; then
+	log_green "Finished uploading the Docker image."
+    else
+	log_red "Failed uploading the Docker image."
+    fi
+    # Call trampoline_after_upload_hook if it's defined.
+    if function_exists trampoline_after_upload_hook; then
+	trampoline_after_upload_hook
+    fi
+
+fi
+
+exit "${test_retval}"
diff --git a/.trampolinerc b/.trampolinerc
new file mode 100644
index 0000000..995ee29
--- /dev/null
+++ b/.trampolinerc
@@ -0,0 +1,51 @@
+# 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.
+
+# Template for .trampolinerc
+
+# Add required env vars here.
+required_envvars+=(
+    "STAGING_BUCKET"
+    "V2_STAGING_BUCKET"
+)
+
+# Add env vars which are passed down into the container here.
+pass_down_envvars+=(
+    "STAGING_BUCKET"
+    "V2_STAGING_BUCKET"
+)
+
+# Prevent unintentional override on the default image.
+if [[ "${TRAMPOLINE_IMAGE_UPLOAD:-false}" == "true" ]] && \
+   [[ -z "${TRAMPOLINE_IMAGE:-}" ]]; then
+   echo "Please set TRAMPOLINE_IMAGE if you want to upload the Docker image."
+   exit 1
+fi
+
+# Define the default value if it makes sense.
+if [[ -z "${TRAMPOLINE_IMAGE_UPLOAD:-}" ]]; then
+    TRAMPOLINE_IMAGE_UPLOAD=""
+fi
+
+if [[ -z "${TRAMPOLINE_IMAGE:-}" ]]; then
+    TRAMPOLINE_IMAGE=""
+fi
+
+if [[ -z "${TRAMPOLINE_DOCKERFILE:-}" ]]; then
+    TRAMPOLINE_DOCKERFILE=""
+fi
+
+if [[ -z "${TRAMPOLINE_BUILD_FILE:-}" ]]; then
+    TRAMPOLINE_BUILD_FILE=""
+fi
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index 19a8354..d77c486 100644
--- a/CONTRIBUTING.rst
+++ b/CONTRIBUTING.rst
@@ -80,25 +80,6 @@
 
 .. 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/docs/conf.py b/docs/conf.py
index a53c37d..2f220a4 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -20,12 +20,16 @@
 # documentation root, use os.path.abspath to make it absolute, like shown here.
 sys.path.insert(0, os.path.abspath(".."))
 
+# For plugins that can not read conf.py.
+# See also: https://github.com/docascode/sphinx-docfx-yaml/issues/85
+sys.path.insert(0, os.path.abspath("."))
+
 __version__ = ""
 
 # -- 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
@@ -35,6 +39,7 @@
     "sphinx.ext.autosummary",
     "sphinx.ext.intersphinx",
     "sphinx.ext.coverage",
+    "sphinx.ext.doctest",
     "sphinx.ext.napoleon",
     "sphinx.ext.todo",
     "sphinx.ext.viewcode",
@@ -90,7 +95,12 @@
 
 # List of patterns, relative to source directory, that match files and
 # directories to ignore when looking for source files.
-exclude_patterns = ["_build"]
+exclude_patterns = [
+    "_build",
+    "samples/AUTHORING_GUIDE.md",
+    "samples/CONTRIBUTING.md",
+    "samples/snippets/README.rst",
+]
 
 # The reST default role (used for this markup: `text`) to use for all
 # documents.
diff --git a/scripts/decrypt-secrets.sh b/scripts/decrypt-secrets.sh
index ff599eb..21f6d2a 100755
--- a/scripts/decrypt-secrets.sh
+++ b/scripts/decrypt-secrets.sh
@@ -20,14 +20,27 @@
 # 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/setup.cfg b/setup.cfg
index 5c32e16..0be0b3f 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -7,6 +7,6 @@
     google/
 exclude =
     tests/
-output = pytype_output/
+output = .pytype/
 # Workaround for https://github.com/google/pytype/issues/150
 disable = pyi-error
diff --git a/synth.metadata b/synth.metadata
index 95ac355..9f65b73 100644
--- a/synth.metadata
+++ b/synth.metadata
@@ -3,15 +3,15 @@
     {
       "git": {
         "name": ".",
-        "remote": "https://github.com/googleapis/python-api-core.git",
-        "sha": "c890675dc9ebc084f105be81dc81c048f4f599ea"
+        "remote": "[email protected]:googleapis/python-api-core",
+        "sha": "db8e636f545a8872f959e3f403cfec30ffed6c34"
       }
     },
     {
       "git": {
         "name": "synthtool",
         "remote": "https://github.com/googleapis/synthtool.git",
-        "sha": "303271797a360f8a439203413f13a160f2f5b3b4"
+        "sha": "0c868d49b8e05bc1f299bc773df9eb4ef9ed96e9"
       }
     }
   ]
diff --git a/synth.py b/synth.py
index 1e8abc9..2a525c8 100644
--- a/synth.py
+++ b/synth.py
@@ -25,4 +25,16 @@
 # Add templated files
 # ----------------------------------------------------------------------------
 templated_files = common.py_library(cov_level=100)
-s.move(templated_files, excludes=["noxfile.py", ".flake8", ".coveragerc", "setup.cfg"])
\ No newline at end of file
+s.move(templated_files, excludes=["noxfile.py", ".flake8", ".coveragerc", "setup.cfg"])
+
+# Add pytype support
+s.replace(
+    ".gitignore",
+    """\
+.pytest_cache
+""",
+    """\
+.pytest_cache
+.pytype
+""",
+)