diff --git a/docs/devsite-help/README.md b/docs/devsite-help/README.md new file mode 100644 index 000000000000..f57aa95ccbab --- /dev/null +++ b/docs/devsite-help/README.md @@ -0,0 +1,3 @@ +# Devsite help + +Client libraries help documentation for common support issues and general information. diff --git a/docs/devsite-help/docfx_helper.py b/docs/devsite-help/docfx_helper.py new file mode 100644 index 000000000000..87d4c521b792 --- /dev/null +++ b/docs/devsite-help/docfx_helper.py @@ -0,0 +1,101 @@ +# Copyright 2026 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 argparse +import pathlib +import shutil +from typing import Dict, Union +import yaml +import pypandoc + +def build_docfx( + current_dir: Union[str, pathlib.Path], + docs_map: Dict[str, str], +) -> None: + current_dir = pathlib.Path(current_dir) + output_dir = current_dir / "docs" / "_build" + + if output_dir.exists(): + shutil.rmtree(output_dir) + output_dir.mkdir(parents=True) + + # Ensure pandoc is available (pypandoc will download it if not found in PATH) + try: + pypandoc.get_pandoc_version() + except OSError: + print("Pandoc not found. Downloading...") + pypandoc.download_pandoc() + + doc_items = [] + + for title, source in docs_map.items(): + source_path = pathlib.Path(source) + if not source_path.is_absolute(): + source_path = current_dir / source_path + + filename = source_path.name + + if filename.endswith(".rst"): + target_filename = filename.replace(".rst", ".md") + print(f"Converting {filename} -> {target_filename} using pandoc") + if source_path.exists(): + # Use pandoc to convert RST to GFM (GitHub Flavored Markdown) + output = pypandoc.convert_file( + str(source_path), + 'gfm', + format='rst' + ) + (output_dir / target_filename).write_text(output, encoding="utf-8") + else: + print(f"Warning: Source {source_path} not found.") + (output_dir / target_filename).write_text(f"# {title}\n\nContent missing.") + href = target_filename + else: + print(f"Copying {filename}") + if source_path.exists(): + shutil.copy(source_path, output_dir / filename) + else: + print(f"Warning: Source {source_path} not found.") + (output_dir / filename).write_text(f"# {title}\n\nContent missing.") + href = filename + + doc_items.append({"name": title, "href": href}) + + # Create the structured TOC + toc = [ + { + "uid": "product-neutral-guides", + "name": "Client library help", + "items": doc_items + } + ] + + # Write toc.yaml + toc_path = output_dir / "toc.yaml" + with open(toc_path, "w", encoding="utf-8") as f: + # Using block style for YAML as requested + yaml.dump(toc, f, default_flow_style=False) + + print(f"DocFX build complete in {output_dir}") + print(f"Generated TOC: {toc}") + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Build DocFX documentation.") + parser.add_argument("--current-dir", required=True, help="Current package directory") + parser.add_argument("--doc", action="append", nargs=2, metavar=("TITLE", "PATH"), help="Add a document title and its source path") + + args = parser.parse_args() + + docs_map = {title: path for title, path in args.doc} if args.doc else {} + build_docfx(args.current_dir, docs_map) diff --git a/docs/devsite-help/getting-started.md b/docs/devsite-help/getting-started.md new file mode 100644 index 000000000000..aedf2f36cb4d --- /dev/null +++ b/docs/devsite-help/getting-started.md @@ -0,0 +1,193 @@ +# Getting started with client libraries + +The Google Cloud Libraries for Python are a mixture of handwritten and autogenerated +libraries connecting to Google Cloud services. The handwritten libraries (such +as [google-cloud-firestore](https://docs.cloud.google.com/python/docs/reference/firestore/latest) and +[google-cloud-spanner](https://docs.cloud.google.com/python/docs/reference/spanner/latest)) +are mostly higher level abstractions over the underlying API. See the documentation +for those individual libraries for details; the documentation here is primarily +aimed at the autogenerated libraries. + +If you haven't already found the library for the API you're interested in, please consult +[the list of Python libraries](https://cloud.google.com/python/docs/reference) which shows both the package +name and the link to the library-specific documentation. In particular, each library has: + +- A "getting started" page which lists the client types within that library +- Version history for the library +- API reference documentation + +This page demonstrates using the [google-cloud-translate](https://docs.cloud.google.com/python/docs/reference/translate/latest) +API as a simple example; the steps required for other APIs are very similar. + +## Prerequisites + +All Google Cloud APIs require a Google Cloud project. If you haven't set one up already, +please [create one](https://cloud.google.com/resource-manager/docs/creating-managing-projects). +You'll also need to [enable your chosen API](https://console.cloud.google.com/apis/library) if it hasn't +already been used within that Google Cloud project. + +There are no specific tools required to develop using the Google Cloud Libraries for Python. All +development environments should work, but you should check that you're targeting a +[supported version of Python](https://docs.cloud.google.com/python/docs/supported-python-versions). + +We recommend installing the [gcloud CLI](https://cloud.google.com/sdk/gcloud). + +## Install the library + +All Google Cloud Libraries for Python are available from [PyPI](https://pypi.org) and can be installed +using `pip`. If you wish to install a pre-release version, you can specify the version explicitly +with the installation command. + +The libraries can be installed in any regular environment, including virtual environments (recommended), +containerized applications, and web frameworks like Django or Flask. + +For the translation example, we'll create a new directory, set up a virtual environment, +and install the package. + +Note that for simplicity, the sample code below uses synchronous calls. Most libraries also provide +asynchronous clients (usually prefixed with `Async`) for use in naturally asynchronous environments +using `asyncio`. + +```sh +mkdir TranslationExample +cd TranslationExample +python3 -m venv venv +source venv/bin/activate +pip install google-cloud-translate +``` + +> **Dependencies** +> If you install the library, you may notice +> a lot of transitive dependencies being installed. This is entirely expected, but you may not recognize +> some of those dependencies. The list below is not comprehensive, but highlights some of the packages +> you'll see being installed. +> +> - protobuf: the library supporting the [Protocol Buffers](https://protobuf.dev) serialization format +> - google-api-core: support libraries specifically tailored for the Google Cloud client libraries +> - google-auth: authentication support for Google Cloud credentials +> - grpcio: support for the [gRPC](https://grpc.io/) RPC protocol +> - google-cloud-core: common helpers and support for [long-running operations](https://docs.cloud.google.com/python/docs/reference/google-cloud-core/latest) + +## Create a client + +The first step in making any API calls is to create a client. Some libraries have multiple clients +for operations involving different resources; others have a single client. In the Translation API +we're using, we use `TranslationServiceClient`. + +Clients can be configured in a number of ways, but in many cases the defaults are fine. The most +common reason to use explicit configuration is to use specific credentials for +[authentication](https://cloud.google.com/docs/authentication/use-cases). For this example, we'll just use +[Application Default Credentials (ADC)](https://cloud.google.com/docs/authentication/provide-credentials-adc#local-dev). +To set up ADC in your local environment, follow the instructions in +[Local development environment](https://cloud.google.com/docs/authentication/provide-credentials-adc#local-dev). +When you create a client, it automatically detects and uses these credentials. + +To create a client with default settings: + +```python +from google.cloud import translate_v3 as translate + +client = translate.TranslationServiceClient() +``` + +## Make requests + +The Google Cloud Libraries use [Protocol Buffers](https://protobuf.dev) +to represent requests and responses, with some additional types to make the APIs more +convenient to work with. + +Most APIs are expressed in terms of a single request returning a single response, although +there are also some streaming APIs involving multiple requests and/or multiple responses. +For our Translation API example, we'll create a simple request for the `translate_text` API. + +```python +from google.cloud import translate_v3 as translate + +client = translate.TranslationServiceClient() + +project_id = "your-project-id-here" +location_id = "global" +parent = f"projects/{project_id}/locations/{location_id}" +request = translate.TranslateTextRequest( + contents=["It is raining.", "It is sunny."], + target_language_code="fr-FR", + parent=parent +) + +response = client.translate_text(request=request) +``` + +This example demonstrates a few features: + +- Protocol Buffer messages are instantiated with keyword arguments representing the fields +- Repeated fields (like `contents`) are represented as Python lists +- The `parent` field is a string representation of a resource name. The library provides helper methods + on the client to construct these paths, ensuring you don't need to concern yourself + with the underlying resource name format. + +The Google Cloud Libraries always expose methods accepting an API request object directly, +but they also support passing keyword arguments directly to the method for convenience: + +```python +response = client.translate_text( + contents=["It is raining.", "It is sunny."], + target_language_code="fr-FR", + parent=parent, +) +``` + +The response is also a Protocol Buffers message. You can inspect the structure of the response +by printing it, or by accessing its attributes directly. In this case we'll look at the +`translations` attribute: + +```python +print(f"Translations returned: {len(response.translations)}") +print() +for translation in response.translations: + print(f"Detected language: {translation.detected_language_code}") + print(f"Translated text: {translation.translated_text}") + print() +``` + +This produces output of: + +```text +Translations returned: 2 + +Detected language: en +Translated text: Il pleut. + +Detected language: en +Translated text: Il fait beau. +``` + +The complete code is: + +```python +from google.cloud import translate_v3 as translate + +client = translate.TranslationServiceClient() + +project_id = "your-project-id-here" +location_id = "global" +parent = f"projects/{project_id}/locations/{location_id}" + +request = translate.TranslateTextRequest( + contents=["It is raining.", "It is sunny."], + target_language_code="fr-FR", + parent=parent, +) + +response = client.translate_text(request=request) + +print(f"Translations returned: {len(response.translations)}") +print() +for translation in response.translations: + print(f"Detected language: {translation.detected_language_code}") + print(f"Translated text: {translation.translated_text}") + print() +``` + +This is just a simple example, which hasn't touched on features like pagination +or specifying call configurations like timeouts and retries. + diff --git a/docs/devsite-help/noxfile.py b/docs/devsite-help/noxfile.py new file mode 100644 index 000000000000..1f8dc4ba5d99 --- /dev/null +++ b/docs/devsite-help/noxfile.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2026 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 pathlib +import nox + +DEFAULT_PYTHON_VERSION = "3.14" +CURRENT_DIRECTORY = pathlib.Path(__file__).parent.absolute() +REPO_ROOT = CURRENT_DIRECTORY.parent.parent + +# Hardcoded dictionary of documentation files. +# Format: {"Display Title": "filename.md" or absolute path} +DOCS_MAP = { + "Getting started": str(CURRENT_DIRECTORY / "getting-started.md"), +} + +nox.options.sessions = [ + "lint", + "lint_setup_py", + "unit", + "docfx", +] + +# Error if a python version is missing +nox.options.error_on_missing_interpreters = True + +@nox.session(python=DEFAULT_PYTHON_VERSION) +def lint_setup_py(session: nox.Session) -> None: + """Verify that setup.py is valid.""" + session.install("setuptools") + session.run("python", "setup.py", "check", "--strict") + +@nox.session(python=DEFAULT_PYTHON_VERSION) +def unit(session: nox.Session) -> None: + """Run unit tests.""" + session.install("pytest", "pytest-cov") + session.install("-e", ".") + session.run("pytest", "tests") + +@nox.session(python=DEFAULT_PYTHON_VERSION) +def lint(session: nox.Session) -> None: + """Run linters.""" + session.install("ruff") + session.run("ruff", "check", ".") + +@nox.session(python=DEFAULT_PYTHON_VERSION) +def docfx(session: nox.Session) -> None: + """Build the docfx yaml files for this library.""" + session.install("PyYAML", "pypandoc") + + # Construct arguments for the helper script + args = [ + "--current-dir", str(CURRENT_DIRECTORY), + ] + for title, source in DOCS_MAP.items(): + args.extend(["--doc", title, str(source)]) + + session.run("python", "docfx_helper.py", *args) +