From 7dca4f2c65a69e281b3b7661bc57518d7fda56d1 Mon Sep 17 00:00:00 2001 From: Juan Lara Date: Thu, 16 Jun 2022 19:32:20 +0000 Subject: [PATCH 01/52] docs(samples): add samples for IN, NOT_IN, and != operators. (#312) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * docs: add samples for IN, NOT_IN, and != operators. * Update samples/snippets_test.py Co-authored-by: Mariatta Wijaya * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * Add an index.yaml file with required indexes * Fix linting errors. Remove unused libraries. * Opt into build specific gcloud projects. * Typo in code comment * Add a test fixture to set up a required index. * Remove snippets that require an index. Will add them in another PR. * Fix linting error * Create a snippets subdirectory under samples * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * Use build-specific gcloud projects for noxfile. * Restore noxfile.py to owlbot version. Add noxfile_config.py Co-authored-by: Mariatta Wijaya Co-authored-by: Owl Bot --- datastore/samples/snippets/noxfile.py | 312 ++++++++++++++++++ datastore/samples/snippets/noxfile_config.py | 42 +++ .../samples/snippets/requirements-test.txt | 4 + datastore/samples/snippets/requirements.txt | 1 + datastore/samples/snippets/snippets.py | 79 +++++ datastore/samples/snippets/snippets_test.py | 68 ++++ 6 files changed, 506 insertions(+) create mode 100644 datastore/samples/snippets/noxfile.py create mode 100644 datastore/samples/snippets/noxfile_config.py create mode 100644 datastore/samples/snippets/requirements-test.txt create mode 100644 datastore/samples/snippets/requirements.txt create mode 100644 datastore/samples/snippets/snippets.py create mode 100644 datastore/samples/snippets/snippets_test.py diff --git a/datastore/samples/snippets/noxfile.py b/datastore/samples/snippets/noxfile.py new file mode 100644 index 00000000000..38bb0a572b8 --- /dev/null +++ b/datastore/samples/snippets/noxfile.py @@ -0,0 +1,312 @@ +# 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 +# +# 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 __future__ import print_function + +import glob +import os +from pathlib import Path +import sys +from typing import Callable, Dict, List, Optional + +import nox + + +# WARNING - WARNING - WARNING - WARNING - WARNING +# WARNING - WARNING - WARNING - WARNING - WARNING +# DO NOT EDIT THIS FILE EVER! +# WARNING - WARNING - WARNING - WARNING - WARNING +# WARNING - WARNING - WARNING - WARNING - WARNING + +BLACK_VERSION = "black==22.3.0" +ISORT_VERSION = "isort==5.10.1" + +# Copy `noxfile_config.py` to your directory and modify it instead. + +# `TEST_CONFIG` dict is a configuration hook that allows users to +# modify the test configurations. The values here should be in sync +# with `noxfile_config.py`. Users will copy `noxfile_config.py` into +# their directory and modify it. + +TEST_CONFIG = { + # You can opt out from the test for specific Python versions. + "ignored_versions": [], + # 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 + # to use your own Cloud project. + "gcloud_project_env": "GOOGLE_CLOUD_PROJECT", + # 'gcloud_project_env': 'BUILD_SPECIFIC_GCLOUD_PROJECT', + # If you need to use a specific version of pip, + # change pip_version_override to the string representation + # of the version number, for example, "20.2.4" + "pip_version_override": None, + # A dictionary you want to inject into your test. Don't put any + # secrets here. These values will override predefined values. + "envs": {}, +} + + +try: + # Ensure we can import noxfile_config in the project's directory. + sys.path.append(".") + from noxfile_config import TEST_CONFIG_OVERRIDE +except ImportError as e: + print("No user noxfile_config found: detail: {}".format(e)) + TEST_CONFIG_OVERRIDE = {} + +# Update the TEST_CONFIG with the user supplied values. +TEST_CONFIG.update(TEST_CONFIG_OVERRIDE) + + +def get_pytest_env_vars() -> Dict[str, str]: + """Returns a dict for pytest invocation.""" + ret = {} + + # Override the GCLOUD_PROJECT and the alias. + env_key = TEST_CONFIG["gcloud_project_env"] + # This should error out if not set. + ret["GOOGLE_CLOUD_PROJECT"] = os.environ[env_key] + + # Apply user supplied envs. + ret.update(TEST_CONFIG["envs"]) + return ret + + +# DO NOT EDIT - automatically generated. +# All versions used to test samples. +ALL_VERSIONS = ["3.6", "3.7", "3.8", "3.9", "3.10"] + +# Any default versions that should be ignored. +IGNORED_VERSIONS = TEST_CONFIG["ignored_versions"] + +TESTED_VERSIONS = sorted([v for v in ALL_VERSIONS if v not in IGNORED_VERSIONS]) + +INSTALL_LIBRARY_FROM_SOURCE = os.environ.get("INSTALL_LIBRARY_FROM_SOURCE", False) in ( + "True", + "true", +) + +# Error if a python version is missing +nox.options.error_on_missing_interpreters = True + +# +# Style Checks +# + + +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 + properly checked. + """ + file_ext_pairs = [os.path.splitext(path) for path in os.listdir(start_dir)] + return [ + basename + for basename, extension in file_ext_pairs + if extension == ".py" + or os.path.isdir(os.path.join(start_dir, basename)) + and basename not in ("__pycache__") + ] + + +# Linting with flake8. +# +# We ignore the following rules: +# E203: whitespace before ‘:’ +# E266: too many leading ‘#’ for block comment +# E501: line too long +# I202: Additional newline in a section of imports +# +# We also need to specify the rules which are ignored by default: +# ['E226', 'W504', 'E126', 'E123', 'W503', 'E24', 'E704', 'E121'] +FLAKE8_COMMON_ARGS = [ + "--show-source", + "--builtin=gettext", + "--max-complexity=20", + "--import-order-style=google", + "--exclude=.nox,.cache,env,lib,generated_pb2,*_pb2.py,*_pb2_grpc.py", + "--ignore=E121,E123,E126,E203,E226,E24,E266,E501,E704,W503,W504,I202", + "--max-line-length=88", +] + + +@nox.session +def lint(session: nox.sessions.Session) -> None: + 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 + [ + "--application-import-names", + ",".join(local_names), + ".", + ] + session.run("flake8", *args) + + +# +# Black +# + + +@nox.session +def blacken(session: nox.sessions.Session) -> None: + """Run black. Format code to uniform standard.""" + session.install(BLACK_VERSION) + python_files = [path for path in os.listdir(".") if path.endswith(".py")] + + session.run("black", *python_files) + + +# +# format = isort + black +# + +@nox.session +def format(session: nox.sessions.Session) -> None: + """ + Run isort to sort imports. Then run black + to format code to uniform standard. + """ + session.install(BLACK_VERSION, ISORT_VERSION) + python_files = [path for path in os.listdir(".") if path.endswith(".py")] + + # Use the --fss option to sort imports using strict alphabetical order. + # See https://pycqa.github.io/isort/docs/configuration/options.html#force-sort-within-sections + session.run("isort", "--fss", *python_files) + session.run("black", *python_files) + + +# +# Sample Tests +# + + +PYTEST_COMMON_ARGS = ["--junitxml=sponge_log.xml"] + + +def _session_tests( + session: nox.sessions.Session, post_install: Callable = None +) -> None: + # check for presence of tests + test_list = glob.glob("*_test.py") + glob.glob("test_*.py") + test_list.extend(glob.glob("tests")) + + if len(test_list) == 0: + print("No tests found, skipping directory.") + return + + if TEST_CONFIG["pip_version_override"]: + pip_version = TEST_CONFIG["pip_version_override"] + session.install(f"pip=={pip_version}") + """Runs py.test for a particular project.""" + concurrent_args = [] + if os.path.exists("requirements.txt"): + if os.path.exists("constraints.txt"): + session.install("-r", "requirements.txt", "-c", "constraints.txt") + else: + session.install("-r", "requirements.txt") + with open("requirements.txt") as rfile: + packages = rfile.read() + + if os.path.exists("requirements-test.txt"): + if os.path.exists("constraints-test.txt"): + session.install( + "-r", "requirements-test.txt", "-c", "constraints-test.txt" + ) + else: + session.install("-r", "requirements-test.txt") + with open("requirements-test.txt") as rtfile: + packages += rtfile.read() + + if INSTALL_LIBRARY_FROM_SOURCE: + session.install("-e", _get_repo_root()) + + if post_install: + post_install(session) + + if "pytest-parallel" in packages: + concurrent_args.extend(['--workers', 'auto', '--tests-per-worker', 'auto']) + elif "pytest-xdist" in packages: + concurrent_args.extend(['-n', 'auto']) + + session.run( + "pytest", + *(PYTEST_COMMON_ARGS + session.posargs + concurrent_args), + # Pytest will return 5 when no tests are collected. This can happen + # on travis where slow and flaky tests are excluded. + # See http://doc.pytest.org/en/latest/_modules/_pytest/main.html + success_codes=[0, 5], + env=get_pytest_env_vars(), + ) + + +@nox.session(python=ALL_VERSIONS) +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) + else: + session.skip( + "SKIPPED: {} tests are disabled for this sample.".format(session.python) + ) + + +# +# Readmegen +# + + +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()) + for i in range(10): + if p is None: + 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.") + + +GENERATED_READMES = sorted([x for x in Path(".").rglob("*.rst.in")]) + + +@nox.session +@nox.parametrize("path", GENERATED_READMES) +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) + + if os.path.exists(os.path.join(dir_, "requirements.txt")): + session.install("-r", os.path.join(dir_, "requirements.txt")) + + in_file = os.path.join(dir_, "README.rst.in") + session.run( + "python", _get_repo_root() + "/scripts/readme-gen/readme_gen.py", in_file + ) diff --git a/datastore/samples/snippets/noxfile_config.py b/datastore/samples/snippets/noxfile_config.py new file mode 100644 index 00000000000..7bf43541d12 --- /dev/null +++ b/datastore/samples/snippets/noxfile_config.py @@ -0,0 +1,42 @@ +# Copyright 2022 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. + +# Default TEST_CONFIG_OVERRIDE for python repos. + +# You can copy this file into your directory, then it will be imported from +# the noxfile.py. + +# The source of truth: +# https://github.com/GoogleCloudPlatform/python-docs-samples/blob/main/noxfile_config.py + +TEST_CONFIG_OVERRIDE = { + # You can opt out from the test for specific Python versions. + "ignored_versions": [], + # 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 + # to use your own Cloud project. + "gcloud_project_env": "BUILD_SPECIFIC_GCLOUD_PROJECT", + # 'gcloud_project_env': 'BUILD_SPECIFIC_GCLOUD_PROJECT', + # If you need to use a specific version of pip, + # change pip_version_override to the string representation + # of the version number, for example, "20.2.4" + "pip_version_override": None, + # A dictionary you want to inject into your test. Don't put any + # secrets here. These values will override predefined values. + "envs": {}, +} diff --git a/datastore/samples/snippets/requirements-test.txt b/datastore/samples/snippets/requirements-test.txt new file mode 100644 index 00000000000..f15210891bc --- /dev/null +++ b/datastore/samples/snippets/requirements-test.txt @@ -0,0 +1,4 @@ +backoff==1.11.1; python_version < "3.7" +backoff==2.0.0; python_version >= "3.7" +pytest==7.0.1 +flaky==3.7.0 \ No newline at end of file diff --git a/datastore/samples/snippets/requirements.txt b/datastore/samples/snippets/requirements.txt new file mode 100644 index 00000000000..d5e16c3771a --- /dev/null +++ b/datastore/samples/snippets/requirements.txt @@ -0,0 +1 @@ +google-cloud-datastore==2.7.0 \ No newline at end of file diff --git a/datastore/samples/snippets/snippets.py b/datastore/samples/snippets/snippets.py new file mode 100644 index 00000000000..40dd5aad595 --- /dev/null +++ b/datastore/samples/snippets/snippets.py @@ -0,0 +1,79 @@ +# Copyright 2022 Google, Inc. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import argparse +from pprint import pprint + +from google.cloud import datastore # noqa: I100 + + +def _preamble(): + # [START datastore_size_coloration_query] + from google.cloud import datastore + + # For help authenticating your client, visit + # https://cloud.google.com/docs/authentication/getting-started + client = datastore.Client() + + # [END datastore_size_coloration_query] + assert client is not None + + +def in_query(client): + # [START datastore_in_query] + query = client.query(kind="Task") + query.add_filter("tag", "IN", ["learn", "study"]) + # [END datastore_in_query] + + return list(query.fetch()) + + +def not_equals_query(client): + # [START datastore_not_equals_query] + query = client.query(kind="Task") + query.add_filter("category", "!=", "work") + # [END datastore_not_equals_query] + + return list(query.fetch()) + + +def not_in_query(client): + # [START datastore_not_in_query] + query = client.query(kind="Task") + query.add_filter("category", "NOT_IN", ["work", "chores", "school"]) + # [END datastore_not_in_query] + + return list(query.fetch()) + + +def main(project_id): + client = datastore.Client(project_id) + + for name, function in globals().items(): + if name in ("main", "_preamble", "defaultdict") or not callable(function): + continue + + print(name) + pprint(function(client)) + print("\n-----------------\n") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description="Demonstrates datastore API operations." + ) + parser.add_argument("project_id", help="Your cloud project ID.") + + args = parser.parse_args() + + main(args.project_id) diff --git a/datastore/samples/snippets/snippets_test.py b/datastore/samples/snippets/snippets_test.py new file mode 100644 index 00000000000..27607c07c12 --- /dev/null +++ b/datastore/samples/snippets/snippets_test.py @@ -0,0 +1,68 @@ +# Copyright 2022 Google, Inc. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os + +import backoff +from google.cloud import datastore + + +import pytest + +import snippets + +PROJECT = os.environ["GOOGLE_CLOUD_PROJECT"] + + +class CleanupClient(datastore.Client): + def __init__(self, *args, **kwargs): + super(CleanupClient, self).__init__(*args, **kwargs) + self.entities_to_delete = [] + self.keys_to_delete = [] + + def cleanup(self): + with self.batch(): + self.delete_multi( + list(set([x.key for x in self.entities_to_delete if x])) + + list(set(self.keys_to_delete)) + ) + + +@pytest.fixture +def client(): + client = CleanupClient(PROJECT) + yield client + client.cleanup() + + +@pytest.mark.flaky +class TestDatastoreSnippets: + # These tests mostly just test the absence of exceptions. + + @backoff.on_exception(backoff.expo, AssertionError, max_time=240) + def test_in_query(self, client): + tasks = snippets.in_query(client) + client.entities_to_delete.extend(tasks) + assert tasks is not None + + @backoff.on_exception(backoff.expo, AssertionError, max_time=240) + def test_not_equals_query(self, client): + tasks = snippets.not_equals_query(client) + client.entities_to_delete.extend(tasks) + assert tasks is not None + + @backoff.on_exception(backoff.expo, AssertionError, max_time=240) + def test_not_in_query(self, client): + tasks = snippets.not_in_query(client) + client.entities_to_delete.extend(tasks) + assert tasks is not None From 4165569e62015899753c8183d8314291923e3023 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Sun, 10 Jul 2022 06:52:08 -0400 Subject: [PATCH 02/52] fix: require python 3.7+ (#332) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore(python): drop python 3.6 Source-Link: https://github.com/googleapis/synthtool/commit/4f89b13af10d086458f9b379e56a614f9d6dab7b Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-python:latest@sha256:e7bb19d47c13839fe8c147e50e02e8b6cf5da8edd1af8b82208cd6f66cc2829c * add api_description to .repo-metadata.json * require python 3.7+ in setup.py * remove python 3.6 sample configs * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * remove python 3.6 from noxfile * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * remove python 3.6 from noxfile * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * ci: update replacement in owlbot.py * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md Co-authored-by: Owl Bot Co-authored-by: Anthonios Partheniou --- datastore/samples/snippets/noxfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datastore/samples/snippets/noxfile.py b/datastore/samples/snippets/noxfile.py index 38bb0a572b8..5fcb9d7461f 100644 --- a/datastore/samples/snippets/noxfile.py +++ b/datastore/samples/snippets/noxfile.py @@ -89,7 +89,7 @@ def get_pytest_env_vars() -> Dict[str, str]: # DO NOT EDIT - automatically generated. # All versions used to test samples. -ALL_VERSIONS = ["3.6", "3.7", "3.8", "3.9", "3.10"] +ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10"] # Any default versions that should be ignored. IGNORED_VERSIONS = TEST_CONFIG["ignored_versions"] From b8fc4a7548d774ea6766123e3e76ad09b78d671f Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Tue, 2 Aug 2022 15:00:11 +0200 Subject: [PATCH 03/52] chore(deps): update all dependencies (#340) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore(deps): update all dependencies * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * revert * revert Co-authored-by: Owl Bot Co-authored-by: Anthonios Partheniou --- datastore/samples/snippets/requirements-test.txt | 8 ++++---- datastore/samples/snippets/requirements.txt | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/datastore/samples/snippets/requirements-test.txt b/datastore/samples/snippets/requirements-test.txt index f15210891bc..9fdbe021770 100644 --- a/datastore/samples/snippets/requirements-test.txt +++ b/datastore/samples/snippets/requirements-test.txt @@ -1,4 +1,4 @@ -backoff==1.11.1; python_version < "3.7" -backoff==2.0.0; python_version >= "3.7" -pytest==7.0.1 -flaky==3.7.0 \ No newline at end of file +backoff===1.11.1; python_version < "3.7" +backoff==2.1.2; python_version >= "3.7" +pytest==7.1.2 +flaky==3.7.0 diff --git a/datastore/samples/snippets/requirements.txt b/datastore/samples/snippets/requirements.txt index d5e16c3771a..4e48d18693f 100644 --- a/datastore/samples/snippets/requirements.txt +++ b/datastore/samples/snippets/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.7.0 \ No newline at end of file +google-cloud-datastore==2.8.0 \ No newline at end of file From 9ac64db7e4da34227e6220950d570b4da750c9bf Mon Sep 17 00:00:00 2001 From: Mariatta Wijaya Date: Tue, 9 Aug 2022 13:30:46 -0700 Subject: [PATCH 04/52] docs: Move the schedule_export samples from python-docs-samples (#344) Moved from https://github.com/GoogleCloudPlatform/python-docs-samples/tree/main/datastore/schedule-export --- .../snippets/schedule-export/README.md | 5 + .../samples/snippets/schedule-export/main.py | 57 ++++ .../snippets/schedule-export/noxfile.py | 312 ++++++++++++++++++ .../schedule-export/noxfile_config.py | 42 +++ .../schedule-export/requirements-test.txt | 1 + .../snippets/schedule-export/requirements.txt | 1 + .../schedule-export/schedule_export_test.py | 73 ++++ 7 files changed, 491 insertions(+) create mode 100644 datastore/samples/snippets/schedule-export/README.md create mode 100644 datastore/samples/snippets/schedule-export/main.py create mode 100644 datastore/samples/snippets/schedule-export/noxfile.py create mode 100644 datastore/samples/snippets/schedule-export/noxfile_config.py create mode 100644 datastore/samples/snippets/schedule-export/requirements-test.txt create mode 100644 datastore/samples/snippets/schedule-export/requirements.txt create mode 100644 datastore/samples/snippets/schedule-export/schedule_export_test.py diff --git a/datastore/samples/snippets/schedule-export/README.md b/datastore/samples/snippets/schedule-export/README.md new file mode 100644 index 00000000000..a8501cddc34 --- /dev/null +++ b/datastore/samples/snippets/schedule-export/README.md @@ -0,0 +1,5 @@ +# Scheduling Datastore exports with Cloud Functions and Cloud Scheduler + +This sample application demonstrates how to schedule exports of your Datastore entities. To deploy this sample, see: + +[Scheduling exports](https://cloud.google.com/datastore/docs/schedule-export) diff --git a/datastore/samples/snippets/schedule-export/main.py b/datastore/samples/snippets/schedule-export/main.py new file mode 100644 index 00000000000..5c0eba699d0 --- /dev/null +++ b/datastore/samples/snippets/schedule-export/main.py @@ -0,0 +1,57 @@ +# Copyright 2021 Google LLC All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import base64 +import json +import os + +from google.cloud import datastore_admin_v1 + +project_id = os.environ.get("GCP_PROJECT") +client = datastore_admin_v1.DatastoreAdminClient() + + +def datastore_export(event, context): + """Triggers a Datastore export from a Cloud Scheduler job. + + Args: + event (dict): event[data] must contain a json object encoded in + base-64. Cloud Scheduler encodes payloads in base-64 by default. + Object must include a 'bucket' value and can include 'kinds' + and 'namespaceIds' values. + context (google.cloud.functions.Context): The Cloud Functions event + metadata. + """ + if "data" in event: + # Triggered via Cloud Scheduler, decode the inner data field of the json payload. + json_data = json.loads(base64.b64decode(event["data"]).decode("utf-8")) + else: + # Otherwise, for instance if triggered via the Cloud Console on a Cloud Function, the event is the data. + json_data = event + + bucket = json_data["bucket"] + entity_filter = datastore_admin_v1.EntityFilter() + + if "kinds" in json_data: + entity_filter.kinds = json_data["kinds"] + + if "namespaceIds" in json_data: + entity_filter.namespace_ids = json_data["namespaceIds"] + + export_request = datastore_admin_v1.ExportEntitiesRequest( + project_id=project_id, output_url_prefix=bucket, entity_filter=entity_filter + ) + operation = client.export_entities(request=export_request) + response = operation.result() + print(response) diff --git a/datastore/samples/snippets/schedule-export/noxfile.py b/datastore/samples/snippets/schedule-export/noxfile.py new file mode 100644 index 00000000000..5fcb9d7461f --- /dev/null +++ b/datastore/samples/snippets/schedule-export/noxfile.py @@ -0,0 +1,312 @@ +# 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 +# +# 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 __future__ import print_function + +import glob +import os +from pathlib import Path +import sys +from typing import Callable, Dict, List, Optional + +import nox + + +# WARNING - WARNING - WARNING - WARNING - WARNING +# WARNING - WARNING - WARNING - WARNING - WARNING +# DO NOT EDIT THIS FILE EVER! +# WARNING - WARNING - WARNING - WARNING - WARNING +# WARNING - WARNING - WARNING - WARNING - WARNING + +BLACK_VERSION = "black==22.3.0" +ISORT_VERSION = "isort==5.10.1" + +# Copy `noxfile_config.py` to your directory and modify it instead. + +# `TEST_CONFIG` dict is a configuration hook that allows users to +# modify the test configurations. The values here should be in sync +# with `noxfile_config.py`. Users will copy `noxfile_config.py` into +# their directory and modify it. + +TEST_CONFIG = { + # You can opt out from the test for specific Python versions. + "ignored_versions": [], + # 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 + # to use your own Cloud project. + "gcloud_project_env": "GOOGLE_CLOUD_PROJECT", + # 'gcloud_project_env': 'BUILD_SPECIFIC_GCLOUD_PROJECT', + # If you need to use a specific version of pip, + # change pip_version_override to the string representation + # of the version number, for example, "20.2.4" + "pip_version_override": None, + # A dictionary you want to inject into your test. Don't put any + # secrets here. These values will override predefined values. + "envs": {}, +} + + +try: + # Ensure we can import noxfile_config in the project's directory. + sys.path.append(".") + from noxfile_config import TEST_CONFIG_OVERRIDE +except ImportError as e: + print("No user noxfile_config found: detail: {}".format(e)) + TEST_CONFIG_OVERRIDE = {} + +# Update the TEST_CONFIG with the user supplied values. +TEST_CONFIG.update(TEST_CONFIG_OVERRIDE) + + +def get_pytest_env_vars() -> Dict[str, str]: + """Returns a dict for pytest invocation.""" + ret = {} + + # Override the GCLOUD_PROJECT and the alias. + env_key = TEST_CONFIG["gcloud_project_env"] + # This should error out if not set. + ret["GOOGLE_CLOUD_PROJECT"] = os.environ[env_key] + + # Apply user supplied envs. + ret.update(TEST_CONFIG["envs"]) + return ret + + +# DO NOT EDIT - automatically generated. +# All versions used to test samples. +ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10"] + +# Any default versions that should be ignored. +IGNORED_VERSIONS = TEST_CONFIG["ignored_versions"] + +TESTED_VERSIONS = sorted([v for v in ALL_VERSIONS if v not in IGNORED_VERSIONS]) + +INSTALL_LIBRARY_FROM_SOURCE = os.environ.get("INSTALL_LIBRARY_FROM_SOURCE", False) in ( + "True", + "true", +) + +# Error if a python version is missing +nox.options.error_on_missing_interpreters = True + +# +# Style Checks +# + + +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 + properly checked. + """ + file_ext_pairs = [os.path.splitext(path) for path in os.listdir(start_dir)] + return [ + basename + for basename, extension in file_ext_pairs + if extension == ".py" + or os.path.isdir(os.path.join(start_dir, basename)) + and basename not in ("__pycache__") + ] + + +# Linting with flake8. +# +# We ignore the following rules: +# E203: whitespace before ‘:’ +# E266: too many leading ‘#’ for block comment +# E501: line too long +# I202: Additional newline in a section of imports +# +# We also need to specify the rules which are ignored by default: +# ['E226', 'W504', 'E126', 'E123', 'W503', 'E24', 'E704', 'E121'] +FLAKE8_COMMON_ARGS = [ + "--show-source", + "--builtin=gettext", + "--max-complexity=20", + "--import-order-style=google", + "--exclude=.nox,.cache,env,lib,generated_pb2,*_pb2.py,*_pb2_grpc.py", + "--ignore=E121,E123,E126,E203,E226,E24,E266,E501,E704,W503,W504,I202", + "--max-line-length=88", +] + + +@nox.session +def lint(session: nox.sessions.Session) -> None: + 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 + [ + "--application-import-names", + ",".join(local_names), + ".", + ] + session.run("flake8", *args) + + +# +# Black +# + + +@nox.session +def blacken(session: nox.sessions.Session) -> None: + """Run black. Format code to uniform standard.""" + session.install(BLACK_VERSION) + python_files = [path for path in os.listdir(".") if path.endswith(".py")] + + session.run("black", *python_files) + + +# +# format = isort + black +# + +@nox.session +def format(session: nox.sessions.Session) -> None: + """ + Run isort to sort imports. Then run black + to format code to uniform standard. + """ + session.install(BLACK_VERSION, ISORT_VERSION) + python_files = [path for path in os.listdir(".") if path.endswith(".py")] + + # Use the --fss option to sort imports using strict alphabetical order. + # See https://pycqa.github.io/isort/docs/configuration/options.html#force-sort-within-sections + session.run("isort", "--fss", *python_files) + session.run("black", *python_files) + + +# +# Sample Tests +# + + +PYTEST_COMMON_ARGS = ["--junitxml=sponge_log.xml"] + + +def _session_tests( + session: nox.sessions.Session, post_install: Callable = None +) -> None: + # check for presence of tests + test_list = glob.glob("*_test.py") + glob.glob("test_*.py") + test_list.extend(glob.glob("tests")) + + if len(test_list) == 0: + print("No tests found, skipping directory.") + return + + if TEST_CONFIG["pip_version_override"]: + pip_version = TEST_CONFIG["pip_version_override"] + session.install(f"pip=={pip_version}") + """Runs py.test for a particular project.""" + concurrent_args = [] + if os.path.exists("requirements.txt"): + if os.path.exists("constraints.txt"): + session.install("-r", "requirements.txt", "-c", "constraints.txt") + else: + session.install("-r", "requirements.txt") + with open("requirements.txt") as rfile: + packages = rfile.read() + + if os.path.exists("requirements-test.txt"): + if os.path.exists("constraints-test.txt"): + session.install( + "-r", "requirements-test.txt", "-c", "constraints-test.txt" + ) + else: + session.install("-r", "requirements-test.txt") + with open("requirements-test.txt") as rtfile: + packages += rtfile.read() + + if INSTALL_LIBRARY_FROM_SOURCE: + session.install("-e", _get_repo_root()) + + if post_install: + post_install(session) + + if "pytest-parallel" in packages: + concurrent_args.extend(['--workers', 'auto', '--tests-per-worker', 'auto']) + elif "pytest-xdist" in packages: + concurrent_args.extend(['-n', 'auto']) + + session.run( + "pytest", + *(PYTEST_COMMON_ARGS + session.posargs + concurrent_args), + # Pytest will return 5 when no tests are collected. This can happen + # on travis where slow and flaky tests are excluded. + # See http://doc.pytest.org/en/latest/_modules/_pytest/main.html + success_codes=[0, 5], + env=get_pytest_env_vars(), + ) + + +@nox.session(python=ALL_VERSIONS) +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) + else: + session.skip( + "SKIPPED: {} tests are disabled for this sample.".format(session.python) + ) + + +# +# Readmegen +# + + +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()) + for i in range(10): + if p is None: + 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.") + + +GENERATED_READMES = sorted([x for x in Path(".").rglob("*.rst.in")]) + + +@nox.session +@nox.parametrize("path", GENERATED_READMES) +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) + + if os.path.exists(os.path.join(dir_, "requirements.txt")): + session.install("-r", os.path.join(dir_, "requirements.txt")) + + in_file = os.path.join(dir_, "README.rst.in") + session.run( + "python", _get_repo_root() + "/scripts/readme-gen/readme_gen.py", in_file + ) diff --git a/datastore/samples/snippets/schedule-export/noxfile_config.py b/datastore/samples/snippets/schedule-export/noxfile_config.py new file mode 100644 index 00000000000..7bf43541d12 --- /dev/null +++ b/datastore/samples/snippets/schedule-export/noxfile_config.py @@ -0,0 +1,42 @@ +# Copyright 2022 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. + +# Default TEST_CONFIG_OVERRIDE for python repos. + +# You can copy this file into your directory, then it will be imported from +# the noxfile.py. + +# The source of truth: +# https://github.com/GoogleCloudPlatform/python-docs-samples/blob/main/noxfile_config.py + +TEST_CONFIG_OVERRIDE = { + # You can opt out from the test for specific Python versions. + "ignored_versions": [], + # 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 + # to use your own Cloud project. + "gcloud_project_env": "BUILD_SPECIFIC_GCLOUD_PROJECT", + # 'gcloud_project_env': 'BUILD_SPECIFIC_GCLOUD_PROJECT', + # If you need to use a specific version of pip, + # change pip_version_override to the string representation + # of the version number, for example, "20.2.4" + "pip_version_override": None, + # A dictionary you want to inject into your test. Don't put any + # secrets here. These values will override predefined values. + "envs": {}, +} diff --git a/datastore/samples/snippets/schedule-export/requirements-test.txt b/datastore/samples/snippets/schedule-export/requirements-test.txt new file mode 100644 index 00000000000..6a3d7bca679 --- /dev/null +++ b/datastore/samples/snippets/schedule-export/requirements-test.txt @@ -0,0 +1 @@ +pytest==7.1.2 \ No newline at end of file diff --git a/datastore/samples/snippets/schedule-export/requirements.txt b/datastore/samples/snippets/schedule-export/requirements.txt new file mode 100644 index 00000000000..560d22f04b4 --- /dev/null +++ b/datastore/samples/snippets/schedule-export/requirements.txt @@ -0,0 +1 @@ +google-cloud-datastore==2.8.0 diff --git a/datastore/samples/snippets/schedule-export/schedule_export_test.py b/datastore/samples/snippets/schedule-export/schedule_export_test.py new file mode 100644 index 00000000000..48d9147c923 --- /dev/null +++ b/datastore/samples/snippets/schedule-export/schedule_export_test.py @@ -0,0 +1,73 @@ +# Copyright 2019 Google LLC All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import base64 +from unittest.mock import Mock + +import main + +mock_context = Mock() +mock_context.event_id = "617187464135194" +mock_context.timestamp = "2020-04-15T22:09:03.761Z" + + +def test_datastore_export(capsys): + # Test an export without an entity filter + bucket = "gs://my-bucket" + json_string = '{{ "bucket": "{bucket}" }}'.format(bucket=bucket) + + # Encode data like Cloud Scheduler + data = bytes(json_string, "utf-8") + data_encoded = base64.b64encode(data) + event = {"data": data_encoded} + + # Mock the Datastore service + mockDatastore = Mock() + main.client = mockDatastore + + # Call tested function + main.datastore_export(event, mock_context) + out, err = capsys.readouterr() + export_args = mockDatastore.export_entities.call_args[1] + # Assert request includes test values + assert export_args["request"].output_url_prefix == bucket + + +def test_datastore_export_entity_filter(capsys): + # Test an export with an entity filter + bucket = "gs://my-bucket" + kinds = "Users,Tasks" + namespaceIds = "Customer831,Customer157" + json_string = '{{ "bucket": "{bucket}", "kinds": "{kinds}", "namespaceIds": "{namespaceIds}" }}'.format( + bucket=bucket, kinds=kinds, namespaceIds=namespaceIds + ) + + # Encode data like Cloud Scheduler + data = bytes(json_string, "utf-8") + data_encoded = base64.b64encode(data) + event = {"data": data_encoded} + + # Mock the Datastore service + mockDatastore = Mock() + main.client = mockDatastore + + # Call tested function + main.datastore_export(event, mock_context) + out, err = capsys.readouterr() + export_args = mockDatastore.export_entities.call_args[1] + # Assert request includes test values + + assert export_args["request"].output_url_prefix == bucket + assert export_args["request"].entity_filter.kinds == kinds + assert export_args["request"].entity_filter.namespace_ids == namespaceIds From 4c1a86e7d9238e5b095c84464111886ec397dcc2 Mon Sep 17 00:00:00 2001 From: Juan Lara Date: Fri, 12 Aug 2022 16:24:29 +0000 Subject: [PATCH 05/52] docs(samples): Add an example of using read_time in queries and get() (#342) * Add an example of using read_time in queries and get() * Fix test for query_with_readtime --- datastore/samples/snippets/snippets.py | 23 ++++++++++++++++++++- datastore/samples/snippets/snippets_test.py | 6 ++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/datastore/samples/snippets/snippets.py b/datastore/samples/snippets/snippets.py index 40dd5aad595..7d2130a8a7b 100644 --- a/datastore/samples/snippets/snippets.py +++ b/datastore/samples/snippets/snippets.py @@ -12,6 +12,7 @@ # limitations under the License. import argparse +from datetime import datetime, timedelta, timezone from pprint import pprint from google.cloud import datastore # noqa: I100 @@ -56,11 +57,31 @@ def not_in_query(client): return list(query.fetch()) +def query_with_readtime(client): + # [START datastore_snapshot_read] + # Create a read time of 120 seconds in the past + read_time = datetime.now(timezone.utc) - timedelta(seconds=120) + + # Fetch an entity at time read_time + task_key = client.key('Task', 'sampletask') + entity = client.get(task_key, read_time=read_time) + + # Query Task entities at time read_time + query = client.query(kind="Task") + tasks = query.fetch(read_time=read_time, limit=10) + # [END datastore_snapshot_read] + + results = list(tasks) + results.append(entity) + + return results + + def main(project_id): client = datastore.Client(project_id) for name, function in globals().items(): - if name in ("main", "_preamble", "defaultdict") or not callable(function): + if name in ("main", "_preamble", "defaultdict", "datetime", "timezone", "timedelta") or not callable(function): continue print(name) diff --git a/datastore/samples/snippets/snippets_test.py b/datastore/samples/snippets/snippets_test.py index 27607c07c12..58e75a599b8 100644 --- a/datastore/samples/snippets/snippets_test.py +++ b/datastore/samples/snippets/snippets_test.py @@ -66,3 +66,9 @@ def test_not_in_query(self, client): tasks = snippets.not_in_query(client) client.entities_to_delete.extend(tasks) assert tasks is not None + + @backoff.on_exception(backoff.expo, AssertionError, max_time=240) + def test_query_with_readtime(self, client): + tasks = snippets.query_with_readtime(client) + client.entities_to_delete.extend(tasks) + assert tasks is not None From 5ba986528371bb35251f7967d795db9ddd193788 Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Wed, 17 Aug 2022 16:26:43 +0200 Subject: [PATCH 06/52] chore(deps): update dependency google-cloud-datastore to v2.8.1 (#348) --- datastore/samples/snippets/requirements.txt | 2 +- datastore/samples/snippets/schedule-export/requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/datastore/samples/snippets/requirements.txt b/datastore/samples/snippets/requirements.txt index 4e48d18693f..7a47161ee8e 100644 --- a/datastore/samples/snippets/requirements.txt +++ b/datastore/samples/snippets/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.8.0 \ No newline at end of file +google-cloud-datastore==2.8.1 \ No newline at end of file diff --git a/datastore/samples/snippets/schedule-export/requirements.txt b/datastore/samples/snippets/schedule-export/requirements.txt index 560d22f04b4..3b582d56d05 100644 --- a/datastore/samples/snippets/schedule-export/requirements.txt +++ b/datastore/samples/snippets/schedule-export/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.8.0 +google-cloud-datastore==2.8.1 From 9bc603d21928b699878e429fd46e53af8b0a0c7a Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Tue, 6 Sep 2022 17:44:00 +0200 Subject: [PATCH 07/52] chore(deps): update dependency pytest to v7.1.3 (#359) --- datastore/samples/snippets/requirements-test.txt | 2 +- .../samples/snippets/schedule-export/requirements-test.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/datastore/samples/snippets/requirements-test.txt b/datastore/samples/snippets/requirements-test.txt index 9fdbe021770..90b34de7bb0 100644 --- a/datastore/samples/snippets/requirements-test.txt +++ b/datastore/samples/snippets/requirements-test.txt @@ -1,4 +1,4 @@ backoff===1.11.1; python_version < "3.7" backoff==2.1.2; python_version >= "3.7" -pytest==7.1.2 +pytest==7.1.3 flaky==3.7.0 diff --git a/datastore/samples/snippets/schedule-export/requirements-test.txt b/datastore/samples/snippets/schedule-export/requirements-test.txt index 6a3d7bca679..f97bae64aa5 100644 --- a/datastore/samples/snippets/schedule-export/requirements-test.txt +++ b/datastore/samples/snippets/schedule-export/requirements-test.txt @@ -1 +1 @@ -pytest==7.1.2 \ No newline at end of file +pytest==7.1.3 \ No newline at end of file From 926a83eabc136881afef9e1dac7f96296c781334 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Tue, 13 Sep 2022 16:14:24 +0000 Subject: [PATCH 08/52] chore: detect samples tests in nested directories (#360) Source-Link: https://github.com/googleapis/synthtool/commit/50db768f450a50d7c1fd62513c113c9bb96fd434 Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-python:latest@sha256:e09366bdf0fd9c8976592988390b24d53583dd9f002d476934da43725adbb978 --- datastore/samples/snippets/noxfile.py | 4 ++-- datastore/samples/snippets/schedule-export/noxfile.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/datastore/samples/snippets/noxfile.py b/datastore/samples/snippets/noxfile.py index 5fcb9d7461f..0398d72ff69 100644 --- a/datastore/samples/snippets/noxfile.py +++ b/datastore/samples/snippets/noxfile.py @@ -207,8 +207,8 @@ def _session_tests( session: nox.sessions.Session, post_install: Callable = None ) -> None: # check for presence of tests - test_list = glob.glob("*_test.py") + glob.glob("test_*.py") - test_list.extend(glob.glob("tests")) + test_list = glob.glob("**/*_test.py", recursive=True) + glob.glob("**/test_*.py", recursive=True) + test_list.extend(glob.glob("**/tests", recursive=True)) if len(test_list) == 0: print("No tests found, skipping directory.") diff --git a/datastore/samples/snippets/schedule-export/noxfile.py b/datastore/samples/snippets/schedule-export/noxfile.py index 5fcb9d7461f..0398d72ff69 100644 --- a/datastore/samples/snippets/schedule-export/noxfile.py +++ b/datastore/samples/snippets/schedule-export/noxfile.py @@ -207,8 +207,8 @@ def _session_tests( session: nox.sessions.Session, post_install: Callable = None ) -> None: # check for presence of tests - test_list = glob.glob("*_test.py") + glob.glob("test_*.py") - test_list.extend(glob.glob("tests")) + test_list = glob.glob("**/*_test.py", recursive=True) + glob.glob("**/test_*.py", recursive=True) + test_list.extend(glob.glob("**/tests", recursive=True)) if len(test_list) == 0: print("No tests found, skipping directory.") From 8acb74c45348c34b013c657452c12fb9a8013126 Mon Sep 17 00:00:00 2001 From: Juan Lara Date: Mon, 3 Oct 2022 21:32:47 +0000 Subject: [PATCH 09/52] samples: Update the read_time snippet. (#363) Co-authored-by: Anthonios Partheniou --- datastore/samples/snippets/snippets.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/datastore/samples/snippets/snippets.py b/datastore/samples/snippets/snippets.py index 7d2130a8a7b..b37020c066b 100644 --- a/datastore/samples/snippets/snippets.py +++ b/datastore/samples/snippets/snippets.py @@ -58,18 +58,18 @@ def not_in_query(client): def query_with_readtime(client): - # [START datastore_snapshot_read] - # Create a read time of 120 seconds in the past - read_time = datetime.now(timezone.utc) - timedelta(seconds=120) + # [START datastore_stale_read] + # Create a read time of 15 seconds in the past + read_time = datetime.now(timezone.utc) - timedelta(seconds=15) - # Fetch an entity at time read_time + # Fetch an entity with read_time task_key = client.key('Task', 'sampletask') entity = client.get(task_key, read_time=read_time) - # Query Task entities at time read_time + # Query Task entities with read_time query = client.query(kind="Task") tasks = query.fetch(read_time=read_time, limit=10) - # [END datastore_snapshot_read] + # [END datastore_stale_read] results = list(tasks) results.append(entity) From 420626c318822b4f6c9ca5675e5eb1636f53f8e2 Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Tue, 4 Oct 2022 15:35:09 +0200 Subject: [PATCH 10/52] chore(deps): update dependency google-cloud-datastore to v2.8.2 (#369) --- datastore/samples/snippets/requirements.txt | 2 +- datastore/samples/snippets/schedule-export/requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/datastore/samples/snippets/requirements.txt b/datastore/samples/snippets/requirements.txt index 7a47161ee8e..2d1b568e647 100644 --- a/datastore/samples/snippets/requirements.txt +++ b/datastore/samples/snippets/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.8.1 \ No newline at end of file +google-cloud-datastore==2.8.2 \ No newline at end of file diff --git a/datastore/samples/snippets/schedule-export/requirements.txt b/datastore/samples/snippets/schedule-export/requirements.txt index 3b582d56d05..8a7a99765cb 100644 --- a/datastore/samples/snippets/schedule-export/requirements.txt +++ b/datastore/samples/snippets/schedule-export/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.8.1 +google-cloud-datastore==2.8.2 From 5d87c1d7cf32862bd96ae694308bbc3a393ea070 Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Thu, 6 Oct 2022 15:40:06 +0200 Subject: [PATCH 11/52] chore(deps): update dependency backoff to v2.2.1 (#371) --- datastore/samples/snippets/requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datastore/samples/snippets/requirements-test.txt b/datastore/samples/snippets/requirements-test.txt index 90b34de7bb0..1315ece85a4 100644 --- a/datastore/samples/snippets/requirements-test.txt +++ b/datastore/samples/snippets/requirements-test.txt @@ -1,4 +1,4 @@ backoff===1.11.1; python_version < "3.7" -backoff==2.1.2; python_version >= "3.7" +backoff==2.2.1; python_version >= "3.7" pytest==7.1.3 flaky==3.7.0 From db4e1e50cf18108b2f81ee032e47f6f4b6af772e Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Tue, 18 Oct 2022 15:12:44 +0200 Subject: [PATCH 12/52] chore(deps): update dependency google-cloud-datastore to v2.8.3 (#375) --- datastore/samples/snippets/requirements.txt | 2 +- datastore/samples/snippets/schedule-export/requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/datastore/samples/snippets/requirements.txt b/datastore/samples/snippets/requirements.txt index 2d1b568e647..09934d289cb 100644 --- a/datastore/samples/snippets/requirements.txt +++ b/datastore/samples/snippets/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.8.2 \ No newline at end of file +google-cloud-datastore==2.8.3 \ No newline at end of file diff --git a/datastore/samples/snippets/schedule-export/requirements.txt b/datastore/samples/snippets/schedule-export/requirements.txt index 8a7a99765cb..075e3cf24f8 100644 --- a/datastore/samples/snippets/schedule-export/requirements.txt +++ b/datastore/samples/snippets/schedule-export/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.8.2 +google-cloud-datastore==2.8.3 From d2a500ce915dc3be1ce50b1a450a091337e37f5a Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Wed, 19 Oct 2022 03:41:55 +0200 Subject: [PATCH 13/52] chore(deps): update dependency google-cloud-datastore to v2.9.0 (#376) --- datastore/samples/snippets/requirements.txt | 2 +- datastore/samples/snippets/schedule-export/requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/datastore/samples/snippets/requirements.txt b/datastore/samples/snippets/requirements.txt index 09934d289cb..a3caccb020d 100644 --- a/datastore/samples/snippets/requirements.txt +++ b/datastore/samples/snippets/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.8.3 \ No newline at end of file +google-cloud-datastore==2.9.0 \ No newline at end of file diff --git a/datastore/samples/snippets/schedule-export/requirements.txt b/datastore/samples/snippets/schedule-export/requirements.txt index 075e3cf24f8..3918f942b81 100644 --- a/datastore/samples/snippets/schedule-export/requirements.txt +++ b/datastore/samples/snippets/schedule-export/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.8.3 +google-cloud-datastore==2.9.0 From 5b206852f8dd567b2734b7479de7ba350ab7ea60 Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Wed, 26 Oct 2022 12:46:34 +0200 Subject: [PATCH 14/52] chore(deps): update dependency pytest to v7.2.0 (#377) --- datastore/samples/snippets/requirements-test.txt | 2 +- .../samples/snippets/schedule-export/requirements-test.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/datastore/samples/snippets/requirements-test.txt b/datastore/samples/snippets/requirements-test.txt index 1315ece85a4..b34edda3766 100644 --- a/datastore/samples/snippets/requirements-test.txt +++ b/datastore/samples/snippets/requirements-test.txt @@ -1,4 +1,4 @@ backoff===1.11.1; python_version < "3.7" backoff==2.2.1; python_version >= "3.7" -pytest==7.1.3 +pytest==7.2.0 flaky==3.7.0 diff --git a/datastore/samples/snippets/schedule-export/requirements-test.txt b/datastore/samples/snippets/schedule-export/requirements-test.txt index f97bae64aa5..89cb815c988 100644 --- a/datastore/samples/snippets/schedule-export/requirements-test.txt +++ b/datastore/samples/snippets/schedule-export/requirements-test.txt @@ -1 +1 @@ -pytest==7.1.3 \ No newline at end of file +pytest==7.2.0 \ No newline at end of file From d548451e918ab763e7afc10802ae3a32ee937dd4 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Wed, 9 Nov 2022 20:53:05 +0100 Subject: [PATCH 15/52] chore(deps): update dependency google-cloud-datastore to v2.10.0 (#381) --- datastore/samples/snippets/requirements.txt | 2 +- datastore/samples/snippets/schedule-export/requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/datastore/samples/snippets/requirements.txt b/datastore/samples/snippets/requirements.txt index a3caccb020d..bc05d8eea4a 100644 --- a/datastore/samples/snippets/requirements.txt +++ b/datastore/samples/snippets/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.9.0 \ No newline at end of file +google-cloud-datastore==2.10.0 \ No newline at end of file diff --git a/datastore/samples/snippets/schedule-export/requirements.txt b/datastore/samples/snippets/schedule-export/requirements.txt index 3918f942b81..3d3dda713fc 100644 --- a/datastore/samples/snippets/schedule-export/requirements.txt +++ b/datastore/samples/snippets/schedule-export/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.9.0 +google-cloud-datastore==2.10.0 From 882a6417bdea3d2fcefda6a02db4c440ea876eaf Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Thu, 24 Nov 2022 16:34:30 -0800 Subject: [PATCH 16/52] chore(python): drop flake8-import-order in samples noxfile (#387) Source-Link: https://github.com/googleapis/synthtool/commit/6ed3a831cb9ff69ef8a504c353e098ec0192ad93 Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-python:latest@sha256:3abfa0f1886adaf0b83f07cb117b24a639ea1cb9cffe56d43280b977033563eb Co-authored-by: Owl Bot --- datastore/samples/snippets/noxfile.py | 26 +++---------------- .../snippets/schedule-export/noxfile.py | 26 +++---------------- 2 files changed, 6 insertions(+), 46 deletions(-) diff --git a/datastore/samples/snippets/noxfile.py b/datastore/samples/snippets/noxfile.py index 0398d72ff69..f5c32b22789 100644 --- a/datastore/samples/snippets/noxfile.py +++ b/datastore/samples/snippets/noxfile.py @@ -18,7 +18,7 @@ import os from pathlib import Path import sys -from typing import Callable, Dict, List, Optional +from typing import Callable, Dict, Optional import nox @@ -109,22 +109,6 @@ def get_pytest_env_vars() -> Dict[str, str]: # -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 - properly checked. - """ - file_ext_pairs = [os.path.splitext(path) for path in os.listdir(start_dir)] - return [ - basename - for basename, extension in file_ext_pairs - if extension == ".py" - or os.path.isdir(os.path.join(start_dir, basename)) - and basename not in ("__pycache__") - ] - - # Linting with flake8. # # We ignore the following rules: @@ -139,7 +123,6 @@ def _determine_local_import_names(start_dir: str) -> List[str]: "--show-source", "--builtin=gettext", "--max-complexity=20", - "--import-order-style=google", "--exclude=.nox,.cache,env,lib,generated_pb2,*_pb2.py,*_pb2_grpc.py", "--ignore=E121,E123,E126,E203,E226,E24,E266,E501,E704,W503,W504,I202", "--max-line-length=88", @@ -149,14 +132,11 @@ def _determine_local_import_names(start_dir: str) -> List[str]: @nox.session def lint(session: nox.sessions.Session) -> None: if not TEST_CONFIG["enforce_type_hints"]: - session.install("flake8", "flake8-import-order") + session.install("flake8") else: - session.install("flake8", "flake8-import-order", "flake8-annotations") + session.install("flake8", "flake8-annotations") - local_names = _determine_local_import_names(".") args = FLAKE8_COMMON_ARGS + [ - "--application-import-names", - ",".join(local_names), ".", ] session.run("flake8", *args) diff --git a/datastore/samples/snippets/schedule-export/noxfile.py b/datastore/samples/snippets/schedule-export/noxfile.py index 0398d72ff69..f5c32b22789 100644 --- a/datastore/samples/snippets/schedule-export/noxfile.py +++ b/datastore/samples/snippets/schedule-export/noxfile.py @@ -18,7 +18,7 @@ import os from pathlib import Path import sys -from typing import Callable, Dict, List, Optional +from typing import Callable, Dict, Optional import nox @@ -109,22 +109,6 @@ def get_pytest_env_vars() -> Dict[str, str]: # -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 - properly checked. - """ - file_ext_pairs = [os.path.splitext(path) for path in os.listdir(start_dir)] - return [ - basename - for basename, extension in file_ext_pairs - if extension == ".py" - or os.path.isdir(os.path.join(start_dir, basename)) - and basename not in ("__pycache__") - ] - - # Linting with flake8. # # We ignore the following rules: @@ -139,7 +123,6 @@ def _determine_local_import_names(start_dir: str) -> List[str]: "--show-source", "--builtin=gettext", "--max-complexity=20", - "--import-order-style=google", "--exclude=.nox,.cache,env,lib,generated_pb2,*_pb2.py,*_pb2_grpc.py", "--ignore=E121,E123,E126,E203,E226,E24,E266,E501,E704,W503,W504,I202", "--max-line-length=88", @@ -149,14 +132,11 @@ def _determine_local_import_names(start_dir: str) -> List[str]: @nox.session def lint(session: nox.sessions.Session) -> None: if not TEST_CONFIG["enforce_type_hints"]: - session.install("flake8", "flake8-import-order") + session.install("flake8") else: - session.install("flake8", "flake8-import-order", "flake8-annotations") + session.install("flake8", "flake8-annotations") - local_names = _determine_local_import_names(".") args = FLAKE8_COMMON_ARGS + [ - "--application-import-names", - ",".join(local_names), ".", ] session.run("flake8", *args) From bbf0e63f230eb97fcfc93d2d6512e09719ddcdbb Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Thu, 1 Dec 2022 03:35:12 +0100 Subject: [PATCH 17/52] chore(deps): update dependency google-cloud-datastore to v2.11.0 (#389) --- datastore/samples/snippets/requirements.txt | 2 +- datastore/samples/snippets/schedule-export/requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/datastore/samples/snippets/requirements.txt b/datastore/samples/snippets/requirements.txt index bc05d8eea4a..85f7f8ec2d4 100644 --- a/datastore/samples/snippets/requirements.txt +++ b/datastore/samples/snippets/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.10.0 \ No newline at end of file +google-cloud-datastore==2.11.0 \ No newline at end of file diff --git a/datastore/samples/snippets/schedule-export/requirements.txt b/datastore/samples/snippets/schedule-export/requirements.txt index 3d3dda713fc..42fae9cb606 100644 --- a/datastore/samples/snippets/schedule-export/requirements.txt +++ b/datastore/samples/snippets/schedule-export/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.10.0 +google-cloud-datastore==2.11.0 From 9bd061dc7535ca3cbdf67156625d65ee523c6772 Mon Sep 17 00:00:00 2001 From: Mariatta Wijaya Date: Fri, 9 Dec 2022 11:49:58 -0800 Subject: [PATCH 18/52] samples: Add snippets and samples for Count query (#383) * Add samples for Count query * Remove unused variable. * Add count query samples with limit * Fix the stale read test. * Raise ValueError instead of general Exception --- datastore/samples/snippets/snippets.py | 164 +++++++++++++++++++- datastore/samples/snippets/snippets_test.py | 50 +++++- 2 files changed, 210 insertions(+), 4 deletions(-) diff --git a/datastore/samples/snippets/snippets.py b/datastore/samples/snippets/snippets.py index b37020c066b..78e72bca66d 100644 --- a/datastore/samples/snippets/snippets.py +++ b/datastore/samples/snippets/snippets.py @@ -14,6 +14,7 @@ import argparse from datetime import datetime, timedelta, timezone from pprint import pprint +import time from google.cloud import datastore # noqa: I100 @@ -63,7 +64,7 @@ def query_with_readtime(client): read_time = datetime.now(timezone.utc) - timedelta(seconds=15) # Fetch an entity with read_time - task_key = client.key('Task', 'sampletask') + task_key = client.key("Task", "sampletask") entity = client.get(task_key, read_time=read_time) # Query Task entities with read_time @@ -77,11 +78,170 @@ def query_with_readtime(client): return results +def count_query_in_transaction(client): + # [START datastore_count_in_transaction] + task1 = datastore.Entity(client.key("Task", "task1")) + task2 = datastore.Entity(client.key("Task", "task2")) + + task1["owner"] = "john" + task2["owner"] = "john" + + tasks = [task1, task2] + client.put_multi(tasks) + + with client.transaction() as transaction: + + tasks_of_john = client.query(kind="Task") + tasks_of_john.add_filter("owner", "=", "john") + total_tasks_query = client.aggregation_query(tasks_of_john) + + query_result = total_tasks_query.count(alias="tasks_count").fetch() + for task_result in query_result: + tasks_count = task_result[0] + if tasks_count.value < 2: + task3 = datastore.Entity(client.key("Task", "task3")) + task3["owner"] = "john" + transaction.put(task3) + tasks.append(task3) + else: + print(f"Found existing {tasks_count.value} tasks, rolling back") + client.entities_to_delete.extend(tasks) + raise ValueError("User 'John' cannot have more than 2 tasks") + # [END datastore_count_in_transaction] + + +def count_query_on_kind(client): + # [START datastore_count_on_kind] + task1 = datastore.Entity(client.key("Task", "task1")) + task2 = datastore.Entity(client.key("Task", "task2")) + + tasks = [task1, task2] + client.put_multi(tasks) + all_tasks_query = client.query(kind="Task") + all_tasks_count_query = client.aggregation_query(all_tasks_query).count() + query_result = all_tasks_count_query.fetch() + for aggregation_results in query_result: + for aggregation in aggregation_results: + print(f"Total tasks (accessible from default alias) is {aggregation.value}") + # [END datastore_count_on_kind] + return tasks + + +def count_query_with_limit(client): + # [START datastore_count_with_limit] + task1 = datastore.Entity(client.key("Task", "task1")) + task2 = datastore.Entity(client.key("Task", "task2")) + task3 = datastore.Entity(client.key("Task", "task3")) + + tasks = [task1, task2, task3] + client.put_multi(tasks) + all_tasks_query = client.query(kind="Task") + all_tasks_count_query = client.aggregation_query(all_tasks_query).count() + query_result = all_tasks_count_query.fetch(limit=2) + for aggregation_results in query_result: + for aggregation in aggregation_results: + print(f"We have at least {aggregation.value} tasks") + # [END datastore_count_with_limit] + return tasks + + +def count_query_property_filter(client): + # [START datastore_count_with_property_filter] + task1 = datastore.Entity(client.key("Task", "task1")) + task2 = datastore.Entity(client.key("Task", "task2")) + task3 = datastore.Entity(client.key("Task", "task3")) + + task1["done"] = True + task2["done"] = False + task3["done"] = True + + tasks = [task1, task2, task3] + client.put_multi(tasks) + completed_tasks = client.query(kind="Task").add_filter("done", "=", True) + remaining_tasks = client.query(kind="Task").add_filter("done", "=", False) + + completed_tasks_query = client.aggregation_query(query=completed_tasks).count( + alias="total_completed_count" + ) + remaining_tasks_query = client.aggregation_query(query=remaining_tasks).count( + alias="total_remaining_count" + ) + + completed_query_result = completed_tasks_query.fetch() + for aggregation_results in completed_query_result: + for aggregation_result in aggregation_results: + if aggregation_result.alias == "total_completed_count": + print(f"Total completed tasks count is {aggregation_result.value}") + + remaining_query_result = remaining_tasks_query.fetch() + for aggregation_results in remaining_query_result: + for aggregation_result in aggregation_results: + if aggregation_result.alias == "total_remaining_count": + print(f"Total remaining tasks count is {aggregation_result.value}") + # [END datastore_count_with_property_filter] + return tasks + + +def count_query_with_stale_read(client): + + tasks = [task for task in client.query(kind="Task").fetch()] + client.delete_multi(tasks) # ensure the database is empty before starting + + # [START datastore_count_query_with_stale_read] + task1 = datastore.Entity(client.key("Task", "task1")) + task2 = datastore.Entity(client.key("Task", "task2")) + + # Saving two tasks + task1["done"] = True + task2["done"] = False + client.put_multi([task1, task2]) + time.sleep(10) + + past_timestamp = datetime.now( + timezone.utc + ) # we have two tasks in database at this time. + time.sleep(10) + + # Saving third task + task3 = datastore.Entity(client.key("Task", "task3")) + task3["done"] = False + client.put(task3) + + all_tasks = client.query(kind="Task") + all_tasks_count = client.aggregation_query( + query=all_tasks, + ).count(alias="all_tasks_count") + + # Executing aggregation query + query_result = all_tasks_count.fetch() + for aggregation_results in query_result: + for aggregation_result in aggregation_results: + print(f"Latest tasks count is {aggregation_result.value}") + + # Executing aggregation query with past timestamp + tasks_in_past = client.aggregation_query(query=all_tasks).count( + alias="tasks_in_past" + ) + tasks_in_the_past_query_result = tasks_in_past.fetch(read_time=past_timestamp) + for aggregation_results in tasks_in_the_past_query_result: + for aggregation_result in aggregation_results: + print(f"Stale tasks count is {aggregation_result.value}") + # [END datastore_count_query_with_stale_read] + return [task1, task2, task3] + + def main(project_id): client = datastore.Client(project_id) for name, function in globals().items(): - if name in ("main", "_preamble", "defaultdict", "datetime", "timezone", "timedelta") or not callable(function): + if name in ( + "main", + "_preamble", + "defaultdict", + "datetime", + "timezone", + "timedelta", + ) or not callable(function): continue print(name) diff --git a/datastore/samples/snippets/snippets_test.py b/datastore/samples/snippets/snippets_test.py index 58e75a599b8..18bc701ec28 100644 --- a/datastore/samples/snippets/snippets_test.py +++ b/datastore/samples/snippets/snippets_test.py @@ -15,8 +15,6 @@ import backoff from google.cloud import datastore - - import pytest import snippets @@ -72,3 +70,51 @@ def test_query_with_readtime(self, client): tasks = snippets.query_with_readtime(client) client.entities_to_delete.extend(tasks) assert tasks is not None + + @backoff.on_exception(backoff.expo, AssertionError, max_time=240) + def test_count_query_in_transaction(self, client): + with pytest.raises(ValueError) as excinfo: + snippets.count_query_in_transaction(client) + assert "User 'John' cannot have more than 2 tasks" in str(excinfo.value) + + @backoff.on_exception(backoff.expo, AssertionError, max_time=240) + def test_count_query_on_kind(self, capsys, client): + tasks = snippets.count_query_on_kind(client) + captured = capsys.readouterr() + assert ( + captured.out.strip() == "Total tasks (accessible from default alias) is 2" + ) + assert captured.err == "" + + client.entities_to_delete.extend(tasks) + + @backoff.on_exception(backoff.expo, AssertionError, max_time=240) + def test_count_query_with_limit(self, capsys, client): + tasks = snippets.count_query_with_limit(client) + captured = capsys.readouterr() + assert captured.out.strip() == "We have at least 2 tasks" + assert captured.err == "" + + client.entities_to_delete.extend(tasks) + + @backoff.on_exception(backoff.expo, AssertionError, max_time=240) + def test_count_query_property_filter(self, capsys, client): + tasks = snippets.count_query_property_filter(client) + captured = capsys.readouterr() + + assert "Total completed tasks count is 2" in captured.out + assert "Total remaining tasks count is 1" in captured.out + assert captured.err == "" + + client.entities_to_delete.extend(tasks) + + @backoff.on_exception(backoff.expo, AssertionError, max_time=240) + def test_count_query_with_stale_read(self, capsys, client): + tasks = snippets.count_query_with_stale_read(client) + captured = capsys.readouterr() + + assert "Latest tasks count is 3" in captured.out + assert "Stale tasks count is 2" in captured.out + assert captured.err == "" + + client.entities_to_delete.extend(tasks) From 332b10c102fc128a039711d8aea901563e9213c2 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Wed, 4 Jan 2023 23:56:38 +0100 Subject: [PATCH 19/52] chore(deps): update dependency google-cloud-datastore to v2.11.1 (#394) --- datastore/samples/snippets/requirements.txt | 2 +- datastore/samples/snippets/schedule-export/requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/datastore/samples/snippets/requirements.txt b/datastore/samples/snippets/requirements.txt index 85f7f8ec2d4..51f08b253bc 100644 --- a/datastore/samples/snippets/requirements.txt +++ b/datastore/samples/snippets/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.11.0 \ No newline at end of file +google-cloud-datastore==2.11.1 \ No newline at end of file diff --git a/datastore/samples/snippets/schedule-export/requirements.txt b/datastore/samples/snippets/schedule-export/requirements.txt index 42fae9cb606..756096cfb35 100644 --- a/datastore/samples/snippets/schedule-export/requirements.txt +++ b/datastore/samples/snippets/schedule-export/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.11.0 +google-cloud-datastore==2.11.1 From 3a596fffd858bca9bdb864b0c47f747a2bd3516e Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Fri, 6 Jan 2023 12:21:39 -0500 Subject: [PATCH 20/52] chore(python): add support for python 3.11 (#395) Source-Link: https://github.com/googleapis/synthtool/commit/7197a001ffb6d8ce7b0b9b11c280f0c536c1033a Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-python:latest@sha256:c43f1d918bcf817d337aa29ff833439494a158a0831508fda4ec75dc4c0d0320 Co-authored-by: Owl Bot --- datastore/samples/snippets/noxfile.py | 2 +- datastore/samples/snippets/schedule-export/noxfile.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/datastore/samples/snippets/noxfile.py b/datastore/samples/snippets/noxfile.py index f5c32b22789..7c8a63994cb 100644 --- a/datastore/samples/snippets/noxfile.py +++ b/datastore/samples/snippets/noxfile.py @@ -89,7 +89,7 @@ def get_pytest_env_vars() -> Dict[str, str]: # DO NOT EDIT - automatically generated. # All versions used to test samples. -ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10"] +ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10", "3.11"] # Any default versions that should be ignored. IGNORED_VERSIONS = TEST_CONFIG["ignored_versions"] diff --git a/datastore/samples/snippets/schedule-export/noxfile.py b/datastore/samples/snippets/schedule-export/noxfile.py index f5c32b22789..7c8a63994cb 100644 --- a/datastore/samples/snippets/schedule-export/noxfile.py +++ b/datastore/samples/snippets/schedule-export/noxfile.py @@ -89,7 +89,7 @@ def get_pytest_env_vars() -> Dict[str, str]: # DO NOT EDIT - automatically generated. # All versions used to test samples. -ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10"] +ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10", "3.11"] # Any default versions that should be ignored. IGNORED_VERSIONS = TEST_CONFIG["ignored_versions"] From 49582bdf49e02a781a39934482510b8e1b2416c5 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Tue, 10 Jan 2023 18:42:49 +0000 Subject: [PATCH 21/52] chore(deps): update dependency google-cloud-datastore to v2.12.0 (#399) --- datastore/samples/snippets/requirements.txt | 2 +- datastore/samples/snippets/schedule-export/requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/datastore/samples/snippets/requirements.txt b/datastore/samples/snippets/requirements.txt index 51f08b253bc..b60eaea51ae 100644 --- a/datastore/samples/snippets/requirements.txt +++ b/datastore/samples/snippets/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.11.1 \ No newline at end of file +google-cloud-datastore==2.12.0 \ No newline at end of file diff --git a/datastore/samples/snippets/schedule-export/requirements.txt b/datastore/samples/snippets/schedule-export/requirements.txt index 756096cfb35..a35fb2f733b 100644 --- a/datastore/samples/snippets/schedule-export/requirements.txt +++ b/datastore/samples/snippets/schedule-export/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.11.1 +google-cloud-datastore==2.12.0 From c0eded0beaf90cfe2c76cf6dc952e8e8d5dc6dc0 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Sat, 14 Jan 2023 18:14:31 +0000 Subject: [PATCH 22/52] chore(deps): update dependency pytest to v7.2.1 (#403) --- datastore/samples/snippets/requirements-test.txt | 2 +- .../samples/snippets/schedule-export/requirements-test.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/datastore/samples/snippets/requirements-test.txt b/datastore/samples/snippets/requirements-test.txt index b34edda3766..c7b5651ff67 100644 --- a/datastore/samples/snippets/requirements-test.txt +++ b/datastore/samples/snippets/requirements-test.txt @@ -1,4 +1,4 @@ backoff===1.11.1; python_version < "3.7" backoff==2.2.1; python_version >= "3.7" -pytest==7.2.0 +pytest==7.2.1 flaky==3.7.0 diff --git a/datastore/samples/snippets/schedule-export/requirements-test.txt b/datastore/samples/snippets/schedule-export/requirements-test.txt index 89cb815c988..dd3c7330bb9 100644 --- a/datastore/samples/snippets/schedule-export/requirements-test.txt +++ b/datastore/samples/snippets/schedule-export/requirements-test.txt @@ -1 +1 @@ -pytest==7.2.0 \ No newline at end of file +pytest==7.2.1 \ No newline at end of file From 5b29ad68a923a447a88cfaee2196f7e3e46ed250 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Wed, 18 Jan 2023 14:43:21 +0000 Subject: [PATCH 23/52] chore(deps): update dependency google-cloud-datastore to v2.13.0 (#405) --- datastore/samples/snippets/requirements.txt | 2 +- datastore/samples/snippets/schedule-export/requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/datastore/samples/snippets/requirements.txt b/datastore/samples/snippets/requirements.txt index b60eaea51ae..50988b168c2 100644 --- a/datastore/samples/snippets/requirements.txt +++ b/datastore/samples/snippets/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.12.0 \ No newline at end of file +google-cloud-datastore==2.13.0 \ No newline at end of file diff --git a/datastore/samples/snippets/schedule-export/requirements.txt b/datastore/samples/snippets/schedule-export/requirements.txt index a35fb2f733b..f36a896f375 100644 --- a/datastore/samples/snippets/schedule-export/requirements.txt +++ b/datastore/samples/snippets/schedule-export/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.12.0 +google-cloud-datastore==2.13.0 From 35a74b6f3887bdf27a9ef34ed65b74cfbce07174 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Mon, 23 Jan 2023 16:09:43 +0000 Subject: [PATCH 24/52] chore(deps): update dependency google-cloud-datastore to v2.13.1 (#409) --- datastore/samples/snippets/requirements.txt | 2 +- datastore/samples/snippets/schedule-export/requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/datastore/samples/snippets/requirements.txt b/datastore/samples/snippets/requirements.txt index 50988b168c2..0b516b62ced 100644 --- a/datastore/samples/snippets/requirements.txt +++ b/datastore/samples/snippets/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.13.0 \ No newline at end of file +google-cloud-datastore==2.13.1 \ No newline at end of file diff --git a/datastore/samples/snippets/schedule-export/requirements.txt b/datastore/samples/snippets/schedule-export/requirements.txt index f36a896f375..b7428c14278 100644 --- a/datastore/samples/snippets/schedule-export/requirements.txt +++ b/datastore/samples/snippets/schedule-export/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.13.0 +google-cloud-datastore==2.13.1 From f1af17a42d91ff8df17f03437022e47c0380e270 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Tue, 24 Jan 2023 15:05:09 +0000 Subject: [PATCH 25/52] chore(deps): update dependency google-cloud-datastore to v2.13.2 (#411) --- datastore/samples/snippets/requirements.txt | 2 +- datastore/samples/snippets/schedule-export/requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/datastore/samples/snippets/requirements.txt b/datastore/samples/snippets/requirements.txt index 0b516b62ced..21c16a12ab5 100644 --- a/datastore/samples/snippets/requirements.txt +++ b/datastore/samples/snippets/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.13.1 \ No newline at end of file +google-cloud-datastore==2.13.2 \ No newline at end of file diff --git a/datastore/samples/snippets/schedule-export/requirements.txt b/datastore/samples/snippets/schedule-export/requirements.txt index b7428c14278..51cac2bed77 100644 --- a/datastore/samples/snippets/schedule-export/requirements.txt +++ b/datastore/samples/snippets/schedule-export/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.13.1 +google-cloud-datastore==2.13.2 From 063ae13c9fdd69942d0c34f724ad3145056fd8a5 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Wed, 1 Mar 2023 10:06:31 +0000 Subject: [PATCH 26/52] chore(deps): update dependency google-cloud-datastore to v2.14.0 (#423) --- datastore/samples/snippets/requirements.txt | 2 +- datastore/samples/snippets/schedule-export/requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/datastore/samples/snippets/requirements.txt b/datastore/samples/snippets/requirements.txt index 21c16a12ab5..03b7d2f4645 100644 --- a/datastore/samples/snippets/requirements.txt +++ b/datastore/samples/snippets/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.13.2 \ No newline at end of file +google-cloud-datastore==2.14.0 \ No newline at end of file diff --git a/datastore/samples/snippets/schedule-export/requirements.txt b/datastore/samples/snippets/schedule-export/requirements.txt index 51cac2bed77..46e66f8d1bd 100644 --- a/datastore/samples/snippets/schedule-export/requirements.txt +++ b/datastore/samples/snippets/schedule-export/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.13.2 +google-cloud-datastore==2.14.0 From 4b2c1cf1c9293881c7e7b6aab9c5844bb834fa45 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Fri, 3 Mar 2023 21:07:00 +0000 Subject: [PATCH 27/52] chore(deps): update dependency pytest to v7.2.2 (#424) --- datastore/samples/snippets/requirements-test.txt | 2 +- .../samples/snippets/schedule-export/requirements-test.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/datastore/samples/snippets/requirements-test.txt b/datastore/samples/snippets/requirements-test.txt index c7b5651ff67..aa58f1dbefd 100644 --- a/datastore/samples/snippets/requirements-test.txt +++ b/datastore/samples/snippets/requirements-test.txt @@ -1,4 +1,4 @@ backoff===1.11.1; python_version < "3.7" backoff==2.2.1; python_version >= "3.7" -pytest==7.2.1 +pytest==7.2.2 flaky==3.7.0 diff --git a/datastore/samples/snippets/schedule-export/requirements-test.txt b/datastore/samples/snippets/schedule-export/requirements-test.txt index dd3c7330bb9..b705adb655e 100644 --- a/datastore/samples/snippets/schedule-export/requirements-test.txt +++ b/datastore/samples/snippets/schedule-export/requirements-test.txt @@ -1 +1 @@ -pytest==7.2.1 \ No newline at end of file +pytest==7.2.2 \ No newline at end of file From 34854c2a6724910a6904bda382c5803543afee90 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Fri, 17 Mar 2023 00:29:05 +0000 Subject: [PATCH 28/52] chore(deps): update dependency google-cloud-datastore to v2.15.0 (#426) Co-authored-by: Mariatta Wijaya --- datastore/samples/snippets/requirements.txt | 2 +- datastore/samples/snippets/schedule-export/requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/datastore/samples/snippets/requirements.txt b/datastore/samples/snippets/requirements.txt index 03b7d2f4645..9492cd23dd5 100644 --- a/datastore/samples/snippets/requirements.txt +++ b/datastore/samples/snippets/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.14.0 \ No newline at end of file +google-cloud-datastore==2.15.0 \ No newline at end of file diff --git a/datastore/samples/snippets/schedule-export/requirements.txt b/datastore/samples/snippets/schedule-export/requirements.txt index 46e66f8d1bd..b276b14e235 100644 --- a/datastore/samples/snippets/schedule-export/requirements.txt +++ b/datastore/samples/snippets/schedule-export/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.14.0 +google-cloud-datastore==2.15.0 From 40f91e9cc07e512b62f5a8f0e85bd24290f1b0a0 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Thu, 6 Apr 2023 17:10:43 +0100 Subject: [PATCH 29/52] chore(deps): update dependency google-cloud-datastore to v2.15.1 (#431) --- datastore/samples/snippets/requirements.txt | 2 +- datastore/samples/snippets/schedule-export/requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/datastore/samples/snippets/requirements.txt b/datastore/samples/snippets/requirements.txt index 9492cd23dd5..b5827b3587d 100644 --- a/datastore/samples/snippets/requirements.txt +++ b/datastore/samples/snippets/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.15.0 \ No newline at end of file +google-cloud-datastore==2.15.1 \ No newline at end of file diff --git a/datastore/samples/snippets/schedule-export/requirements.txt b/datastore/samples/snippets/schedule-export/requirements.txt index b276b14e235..acd4bf9213f 100644 --- a/datastore/samples/snippets/schedule-export/requirements.txt +++ b/datastore/samples/snippets/schedule-export/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.15.0 +google-cloud-datastore==2.15.1 From 7afd3f96a93759ebc66659a58df467eb373dfcf4 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Tue, 18 Apr 2023 18:01:28 +0200 Subject: [PATCH 30/52] chore(deps): update dependency pytest to v7.3.1 (#433) --- datastore/samples/snippets/requirements-test.txt | 2 +- .../samples/snippets/schedule-export/requirements-test.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/datastore/samples/snippets/requirements-test.txt b/datastore/samples/snippets/requirements-test.txt index aa58f1dbefd..18625156bb7 100644 --- a/datastore/samples/snippets/requirements-test.txt +++ b/datastore/samples/snippets/requirements-test.txt @@ -1,4 +1,4 @@ backoff===1.11.1; python_version < "3.7" backoff==2.2.1; python_version >= "3.7" -pytest==7.2.2 +pytest==7.3.1 flaky==3.7.0 diff --git a/datastore/samples/snippets/schedule-export/requirements-test.txt b/datastore/samples/snippets/schedule-export/requirements-test.txt index b705adb655e..a6510db8d39 100644 --- a/datastore/samples/snippets/schedule-export/requirements-test.txt +++ b/datastore/samples/snippets/schedule-export/requirements-test.txt @@ -1 +1 @@ -pytest==7.2.2 \ No newline at end of file +pytest==7.3.1 \ No newline at end of file From 7e8f38231211434e8fe2178f0abd2f5a05f779b8 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Thu, 1 Jun 2023 12:33:39 +0200 Subject: [PATCH 31/52] chore(deps): update dependency google-cloud-datastore to v2.15.2 (#438) Co-authored-by: meredithslota --- datastore/samples/snippets/requirements.txt | 2 +- datastore/samples/snippets/schedule-export/requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/datastore/samples/snippets/requirements.txt b/datastore/samples/snippets/requirements.txt index b5827b3587d..d0195bcdbdf 100644 --- a/datastore/samples/snippets/requirements.txt +++ b/datastore/samples/snippets/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.15.1 \ No newline at end of file +google-cloud-datastore==2.15.2 \ No newline at end of file diff --git a/datastore/samples/snippets/schedule-export/requirements.txt b/datastore/samples/snippets/schedule-export/requirements.txt index acd4bf9213f..ff812cc4f0c 100644 --- a/datastore/samples/snippets/schedule-export/requirements.txt +++ b/datastore/samples/snippets/schedule-export/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.15.1 +google-cloud-datastore==2.15.2 From 2d9a735e503927c2868b94c8fb56895b1d3f22c4 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Mon, 12 Jun 2023 22:58:15 +0200 Subject: [PATCH 32/52] chore(deps): update dependency pytest to v7.3.2 (#445) --- datastore/samples/snippets/requirements-test.txt | 2 +- .../samples/snippets/schedule-export/requirements-test.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/datastore/samples/snippets/requirements-test.txt b/datastore/samples/snippets/requirements-test.txt index 18625156bb7..d700e917ed7 100644 --- a/datastore/samples/snippets/requirements-test.txt +++ b/datastore/samples/snippets/requirements-test.txt @@ -1,4 +1,4 @@ backoff===1.11.1; python_version < "3.7" backoff==2.2.1; python_version >= "3.7" -pytest==7.3.1 +pytest==7.3.2 flaky==3.7.0 diff --git a/datastore/samples/snippets/schedule-export/requirements-test.txt b/datastore/samples/snippets/schedule-export/requirements-test.txt index a6510db8d39..28706bebc1f 100644 --- a/datastore/samples/snippets/schedule-export/requirements-test.txt +++ b/datastore/samples/snippets/schedule-export/requirements-test.txt @@ -1 +1 @@ -pytest==7.3.1 \ No newline at end of file +pytest==7.3.2 \ No newline at end of file From f8664ee3991deb3311f5b192c96ea98c84a9f69e Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Wed, 5 Jul 2023 16:45:11 +0200 Subject: [PATCH 33/52] chore(deps): update all dependencies (#449) Co-authored-by: Anthonios Partheniou --- datastore/samples/snippets/requirements-test.txt | 2 +- datastore/samples/snippets/requirements.txt | 2 +- .../samples/snippets/schedule-export/requirements-test.txt | 2 +- datastore/samples/snippets/schedule-export/requirements.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/datastore/samples/snippets/requirements-test.txt b/datastore/samples/snippets/requirements-test.txt index d700e917ed7..f8841e8c6af 100644 --- a/datastore/samples/snippets/requirements-test.txt +++ b/datastore/samples/snippets/requirements-test.txt @@ -1,4 +1,4 @@ backoff===1.11.1; python_version < "3.7" backoff==2.2.1; python_version >= "3.7" -pytest==7.3.2 +pytest==7.4.0 flaky==3.7.0 diff --git a/datastore/samples/snippets/requirements.txt b/datastore/samples/snippets/requirements.txt index d0195bcdbdf..fcddcf53dde 100644 --- a/datastore/samples/snippets/requirements.txt +++ b/datastore/samples/snippets/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.15.2 \ No newline at end of file +google-cloud-datastore==2.16.0 \ No newline at end of file diff --git a/datastore/samples/snippets/schedule-export/requirements-test.txt b/datastore/samples/snippets/schedule-export/requirements-test.txt index 28706bebc1f..6950eb5a7b6 100644 --- a/datastore/samples/snippets/schedule-export/requirements-test.txt +++ b/datastore/samples/snippets/schedule-export/requirements-test.txt @@ -1 +1 @@ -pytest==7.3.2 \ No newline at end of file +pytest==7.4.0 \ No newline at end of file diff --git a/datastore/samples/snippets/schedule-export/requirements.txt b/datastore/samples/snippets/schedule-export/requirements.txt index ff812cc4f0c..4c89b8f03eb 100644 --- a/datastore/samples/snippets/schedule-export/requirements.txt +++ b/datastore/samples/snippets/schedule-export/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.15.2 +google-cloud-datastore==2.16.0 From c932e566094028849ec1c7f12a21050b807865bf Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Wed, 5 Jul 2023 21:22:31 +0200 Subject: [PATCH 34/52] chore(deps): update dependency google-cloud-datastore to v2.16.1 (#454) --- datastore/samples/snippets/requirements.txt | 2 +- datastore/samples/snippets/schedule-export/requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/datastore/samples/snippets/requirements.txt b/datastore/samples/snippets/requirements.txt index fcddcf53dde..9c65e4996e4 100644 --- a/datastore/samples/snippets/requirements.txt +++ b/datastore/samples/snippets/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.16.0 \ No newline at end of file +google-cloud-datastore==2.16.1 \ No newline at end of file diff --git a/datastore/samples/snippets/schedule-export/requirements.txt b/datastore/samples/snippets/schedule-export/requirements.txt index 4c89b8f03eb..4a4657a1ac1 100644 --- a/datastore/samples/snippets/schedule-export/requirements.txt +++ b/datastore/samples/snippets/schedule-export/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.16.0 +google-cloud-datastore==2.16.1 From 8164e8d9cfc03552775ffc1d46f3279db9747460 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Wed, 9 Aug 2023 20:56:51 +0200 Subject: [PATCH 35/52] chore(deps): update dependency google-cloud-datastore to v2.17.0 (#469) --- datastore/samples/snippets/requirements.txt | 2 +- datastore/samples/snippets/schedule-export/requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/datastore/samples/snippets/requirements.txt b/datastore/samples/snippets/requirements.txt index 9c65e4996e4..390385f6cb3 100644 --- a/datastore/samples/snippets/requirements.txt +++ b/datastore/samples/snippets/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.16.1 \ No newline at end of file +google-cloud-datastore==2.17.0 \ No newline at end of file diff --git a/datastore/samples/snippets/schedule-export/requirements.txt b/datastore/samples/snippets/schedule-export/requirements.txt index 4a4657a1ac1..f305ba58311 100644 --- a/datastore/samples/snippets/schedule-export/requirements.txt +++ b/datastore/samples/snippets/schedule-export/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.16.1 +google-cloud-datastore==2.17.0 From 4141fee75746c3a3801c3d707d82b274753d0867 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Wed, 6 Sep 2023 00:43:22 +0200 Subject: [PATCH 36/52] chore(deps): update all dependencies (#473) --- datastore/samples/snippets/requirements-test.txt | 2 +- .../samples/snippets/schedule-export/requirements-test.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/datastore/samples/snippets/requirements-test.txt b/datastore/samples/snippets/requirements-test.txt index f8841e8c6af..063f91e2ea3 100644 --- a/datastore/samples/snippets/requirements-test.txt +++ b/datastore/samples/snippets/requirements-test.txt @@ -1,4 +1,4 @@ backoff===1.11.1; python_version < "3.7" backoff==2.2.1; python_version >= "3.7" -pytest==7.4.0 +pytest==7.4.1 flaky==3.7.0 diff --git a/datastore/samples/snippets/schedule-export/requirements-test.txt b/datastore/samples/snippets/schedule-export/requirements-test.txt index 6950eb5a7b6..bd5fef9ba0a 100644 --- a/datastore/samples/snippets/schedule-export/requirements-test.txt +++ b/datastore/samples/snippets/schedule-export/requirements-test.txt @@ -1 +1 @@ -pytest==7.4.0 \ No newline at end of file +pytest==7.4.1 \ No newline at end of file From d30a1a85f67203dd9851562d8e1a721ea0afe445 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Sat, 16 Sep 2023 01:43:08 +0200 Subject: [PATCH 37/52] chore(deps): update all dependencies (#475) --- datastore/samples/snippets/requirements.txt | 2 +- datastore/samples/snippets/schedule-export/requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/datastore/samples/snippets/requirements.txt b/datastore/samples/snippets/requirements.txt index 390385f6cb3..d4e90e37456 100644 --- a/datastore/samples/snippets/requirements.txt +++ b/datastore/samples/snippets/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.17.0 \ No newline at end of file +google-cloud-datastore==2.18.0 \ No newline at end of file diff --git a/datastore/samples/snippets/schedule-export/requirements.txt b/datastore/samples/snippets/schedule-export/requirements.txt index f305ba58311..a84b83a1fe6 100644 --- a/datastore/samples/snippets/schedule-export/requirements.txt +++ b/datastore/samples/snippets/schedule-export/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.17.0 +google-cloud-datastore==2.18.0 From 9277a674afc39417faa16872c9b79b1787161c20 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Mon, 18 Sep 2023 19:29:06 +0200 Subject: [PATCH 38/52] chore(deps): update all dependencies (#483) --- datastore/samples/snippets/requirements-test.txt | 2 +- .../samples/snippets/schedule-export/requirements-test.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/datastore/samples/snippets/requirements-test.txt b/datastore/samples/snippets/requirements-test.txt index 063f91e2ea3..9b6a1aeda14 100644 --- a/datastore/samples/snippets/requirements-test.txt +++ b/datastore/samples/snippets/requirements-test.txt @@ -1,4 +1,4 @@ backoff===1.11.1; python_version < "3.7" backoff==2.2.1; python_version >= "3.7" -pytest==7.4.1 +pytest==7.4.2 flaky==3.7.0 diff --git a/datastore/samples/snippets/schedule-export/requirements-test.txt b/datastore/samples/snippets/schedule-export/requirements-test.txt index bd5fef9ba0a..de1887becf2 100644 --- a/datastore/samples/snippets/schedule-export/requirements-test.txt +++ b/datastore/samples/snippets/schedule-export/requirements-test.txt @@ -1 +1 @@ -pytest==7.4.1 \ No newline at end of file +pytest==7.4.2 \ No newline at end of file From 62b7aecadb8d36c348b0d76e4a3817bb2c44cd87 Mon Sep 17 00:00:00 2001 From: Juan Lara Date: Tue, 3 Oct 2023 18:48:06 +0000 Subject: [PATCH 39/52] samples: Add snippets for sum and avg (#480) --- datastore/samples/snippets/snippets.py | 159 ++++++++++++++++++++ datastore/samples/snippets/snippets_test.py | 95 ++++++++++++ 2 files changed, 254 insertions(+) diff --git a/datastore/samples/snippets/snippets.py b/datastore/samples/snippets/snippets.py index 78e72bca66d..749a1ce4d74 100644 --- a/datastore/samples/snippets/snippets.py +++ b/datastore/samples/snippets/snippets.py @@ -230,6 +230,165 @@ def count_query_with_stale_read(client): return [task1, task2, task3] +def sum_query_on_kind(client): + # [START datastore_sum_aggregation_query_on_kind] + # Set up sample entities + # Use incomplete key to auto-generate ID + task1 = datastore.Entity(client.key("Task")) + task2 = datastore.Entity(client.key("Task")) + task3 = datastore.Entity(client.key("Task")) + + task1["hours"] = 5 + task2["hours"] = 3 + task3["hours"] = 1 + + tasks = [task1, task2, task3] + client.put_multi(tasks) + + # Execute sum aggregation query + all_tasks_query = client.query(kind="Task") + all_tasks_sum_query = client.aggregation_query(all_tasks_query).sum("hours") + query_result = all_tasks_sum_query.fetch() + for aggregation_results in query_result: + for aggregation in aggregation_results: + print(f"Total sum of hours in tasks is {aggregation.value}") + # [END datastore_sum_aggregation_query_on_kind] + return tasks + + +def sum_query_property_filter(client): + # [START datastore_sum_aggregation_query_with_filters] + # Set up sample entities + # Use incomplete key to auto-generate ID + task1 = datastore.Entity(client.key("Task")) + task2 = datastore.Entity(client.key("Task")) + task3 = datastore.Entity(client.key("Task")) + + task1["hours"] = 5 + task2["hours"] = 3 + task3["hours"] = 1 + + task1["done"] = True + task2["done"] = True + task3["done"] = False + + tasks = [task1, task2, task3] + client.put_multi(tasks) + + # Execute sum aggregation query with filters + completed_tasks = client.query(kind="Task").add_filter("done", "=", True) + completed_tasks_query = client.aggregation_query(query=completed_tasks).sum( + property_ref="hours", + alias="total_completed_sum_hours" + ) + + completed_query_result = completed_tasks_query.fetch() + for aggregation_results in completed_query_result: + for aggregation_result in aggregation_results: + if aggregation_result.alias == "total_completed_sum_hours": + print(f"Total sum of hours in completed tasks is {aggregation_result.value}") + # [END datastore_sum_aggregation_query_with_filters] + return tasks + + +def avg_query_on_kind(client): + # [START datastore_avg_aggregation_query_on_kind] + # Set up sample entities + # Use incomplete key to auto-generate ID + task1 = datastore.Entity(client.key("Task")) + task2 = datastore.Entity(client.key("Task")) + task3 = datastore.Entity(client.key("Task")) + + task1["hours"] = 5 + task2["hours"] = 3 + task3["hours"] = 1 + + tasks = [task1, task2, task3] + client.put_multi(tasks) + + # Execute average aggregation query + all_tasks_query = client.query(kind="Task") + all_tasks_avg_query = client.aggregation_query(all_tasks_query).avg("hours") + query_result = all_tasks_avg_query.fetch() + for aggregation_results in query_result: + for aggregation in aggregation_results: + print(f"Total average of hours in tasks is {aggregation.value}") + # [END datastore_avg_aggregation_query_on_kind] + return tasks + + +def avg_query_property_filter(client): + # [START datastore_avg_aggregation_query_with_filters] + # Set up sample entities + # Use incomplete key to auto-generate ID + task1 = datastore.Entity(client.key("Task")) + task2 = datastore.Entity(client.key("Task")) + task3 = datastore.Entity(client.key("Task")) + + task1["hours"] = 5 + task2["hours"] = 3 + task3["hours"] = 1 + + task1["done"] = True + task2["done"] = True + task3["done"] = False + + tasks = [task1, task2, task3] + client.put_multi(tasks) + + # Execute average aggregation query with filters + completed_tasks = client.query(kind="Task").add_filter("done", "=", True) + completed_tasks_query = client.aggregation_query(query=completed_tasks).avg( + property_ref="hours", + alias="total_completed_avg_hours" + ) + + completed_query_result = completed_tasks_query.fetch() + for aggregation_results in completed_query_result: + for aggregation_result in aggregation_results: + if aggregation_result.alias == "total_completed_avg_hours": + print(f"Total average of hours in completed tasks is {aggregation_result.value}") + # [END datastore_avg_aggregation_query_with_filters] + return tasks + + +def multiple_aggregations_query(client): + # [START datastore_multiple_aggregation_in_structured_query] + # Set up sample entities + # Use incomplete key to auto-generate ID + task1 = datastore.Entity(client.key("Task")) + task2 = datastore.Entity(client.key("Task")) + task3 = datastore.Entity(client.key("Task")) + + task1["hours"] = 5 + task2["hours"] = 3 + task3["hours"] = 1 + + tasks = [task1, task2, task3] + client.put_multi(tasks) + + # Execute query with multiple aggregations + all_tasks_query = client.query(kind="Task") + aggregation_query = client.aggregation_query(all_tasks_query) + # Add aggregations + aggregation_query.add_aggregations( + [ + datastore.aggregation.CountAggregation(alias="count_aggregation"), + datastore.aggregation.SumAggregation( + property_ref="hours", alias="sum_aggregation"), + datastore.aggregation.AvgAggregation( + property_ref="hours", alias="avg_aggregation") + ] + ) + + query_result = aggregation_query.fetch() + for aggregation_results in query_result: + for aggregation in aggregation_results: + print(f"{aggregation.alias} value is {aggregation.value}") + # [END datastore_multiple_aggregation_in_structured_query] + return tasks + + def main(project_id): client = datastore.Client(project_id) diff --git a/datastore/samples/snippets/snippets_test.py b/datastore/samples/snippets/snippets_test.py index 18bc701ec28..92db05075d9 100644 --- a/datastore/samples/snippets/snippets_test.py +++ b/datastore/samples/snippets/snippets_test.py @@ -14,7 +14,9 @@ import os import backoff +import google.api_core.exceptions from google.cloud import datastore +from google.cloud import datastore_admin_v1 import pytest import snippets @@ -43,6 +45,38 @@ def client(): client.cleanup() +@pytest.fixture(scope="session", autouse=True) +def setup_indexes(request): + # Set up required indexes + admin_client = datastore_admin_v1.DatastoreAdminClient() + + indexes = [] + done_property_index = datastore_admin_v1.Index.IndexedProperty( + name='done', + direction=datastore_admin_v1.Index.Direction.ASCENDING + ) + hour_property_index = datastore_admin_v1.Index.IndexedProperty( + name='hours', + direction=datastore_admin_v1.Index.Direction.ASCENDING + ) + done_hour_index = datastore_admin_v1.Index( + kind='Task', + ancestor=datastore_admin_v1.Index.AncestorMode.NONE, + properties=[done_property_index, hour_property_index] + ) + indexes.append(done_hour_index) + + for index in indexes: + request = datastore_admin_v1.CreateIndexRequest(project_id=PROJECT, index=index) + # Create the required index + # Dependant tests will fail until the index is ready + try: + admin_client.create_index(request) + # Pass if the index already exists + except (google.api_core.exceptions.AlreadyExists): + pass + + @pytest.mark.flaky class TestDatastoreSnippets: # These tests mostly just test the absence of exceptions. @@ -118,3 +152,64 @@ def test_count_query_with_stale_read(self, capsys, client): assert captured.err == "" client.entities_to_delete.extend(tasks) + + @backoff.on_exception(backoff.expo, AssertionError, max_time=240) + def test_sum_query_on_kind(self, capsys, client): + tasks = snippets.sum_query_on_kind(client) + captured = capsys.readouterr() + assert ( + captured.out.strip() == "Total sum of hours in tasks is 9" + ) + assert captured.err == "" + + client.entities_to_delete.extend(tasks) + + @backoff.on_exception(backoff.expo, AssertionError, max_time=240) + def test_sum_query_property_filter(self, capsys, client): + tasks = snippets.sum_query_property_filter(client) + captured = capsys.readouterr() + assert ( + captured.out.strip() == "Total sum of hours in completed tasks is 8" + ) + assert captured.err == "" + + client.entities_to_delete.extend(tasks) + + @backoff.on_exception(backoff.expo, AssertionError, max_time=240) + def test_avg_query_on_kind(self, capsys, client): + tasks = snippets.avg_query_on_kind(client) + captured = capsys.readouterr() + assert ( + captured.out.strip() == "Total average of hours in tasks is 3.0" + ) + assert captured.err == "" + + client.entities_to_delete.extend(tasks) + + @backoff.on_exception(backoff.expo, AssertionError, max_time=240) + def test_avg_query_property_filter(self, capsys, client): + tasks = snippets.avg_query_property_filter(client) + captured = capsys.readouterr() + assert ( + captured.out.strip() == "Total average of hours in completed tasks is 4.0" + ) + assert captured.err == "" + + client.entities_to_delete.extend(tasks) + + @backoff.on_exception(backoff.expo, AssertionError, max_time=240) + def test_multiple_aggregations_query(self, capsys, client): + tasks = snippets.multiple_aggregations_query(client) + captured = capsys.readouterr() + assert ( + 'avg_aggregation value is 3.0' in captured.out + ) + assert ( + 'count_aggregation value is 3' in captured.out + ) + assert ( + 'sum_aggregation value is 9' in captured.out + ) + assert captured.err == "" + + client.entities_to_delete.extend(tasks) From 9d881ec16e094adeae904b5de960de263f228e69 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Mon, 30 Oct 2023 12:44:35 +0100 Subject: [PATCH 40/52] chore(deps): update all dependencies (#493) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore(deps): update all dependencies * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --------- Co-authored-by: Owl Bot --- datastore/samples/snippets/requirements-test.txt | 2 +- .../samples/snippets/schedule-export/requirements-test.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/datastore/samples/snippets/requirements-test.txt b/datastore/samples/snippets/requirements-test.txt index 9b6a1aeda14..dfba3f35d90 100644 --- a/datastore/samples/snippets/requirements-test.txt +++ b/datastore/samples/snippets/requirements-test.txt @@ -1,4 +1,4 @@ backoff===1.11.1; python_version < "3.7" backoff==2.2.1; python_version >= "3.7" -pytest==7.4.2 +pytest==7.4.3 flaky==3.7.0 diff --git a/datastore/samples/snippets/schedule-export/requirements-test.txt b/datastore/samples/snippets/schedule-export/requirements-test.txt index de1887becf2..f16ee69ae92 100644 --- a/datastore/samples/snippets/schedule-export/requirements-test.txt +++ b/datastore/samples/snippets/schedule-export/requirements-test.txt @@ -1 +1 @@ -pytest==7.4.2 \ No newline at end of file +pytest==7.4.3 \ No newline at end of file From 60ebad6f39c5606c6965aab9d9629e9e00c5c446 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Fri, 1 Dec 2023 14:52:24 -0800 Subject: [PATCH 41/52] feat: Add support for Python 3.12 (#498) * chore(python): Add Python 3.12 Source-Link: https://github.com/googleapis/synthtool/commit/af16e6d4672cc7b400f144de2fc3068b54ff47d2 Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-python:latest@sha256:bacc3af03bff793a03add584537b36b5644342931ad989e3ba1171d3bd5399f5 * Add python 3.12 to setup.py, constraints and required checks --------- Co-authored-by: Owl Bot Co-authored-by: Anthonios Partheniou Co-authored-by: Daniel Sanche --- datastore/samples/snippets/noxfile.py | 2 +- datastore/samples/snippets/schedule-export/noxfile.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/datastore/samples/snippets/noxfile.py b/datastore/samples/snippets/noxfile.py index 7c8a63994cb..483b5590179 100644 --- a/datastore/samples/snippets/noxfile.py +++ b/datastore/samples/snippets/noxfile.py @@ -89,7 +89,7 @@ def get_pytest_env_vars() -> Dict[str, str]: # DO NOT EDIT - automatically generated. # All versions used to test samples. -ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10", "3.11"] +ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"] # Any default versions that should be ignored. IGNORED_VERSIONS = TEST_CONFIG["ignored_versions"] diff --git a/datastore/samples/snippets/schedule-export/noxfile.py b/datastore/samples/snippets/schedule-export/noxfile.py index 7c8a63994cb..483b5590179 100644 --- a/datastore/samples/snippets/schedule-export/noxfile.py +++ b/datastore/samples/snippets/schedule-export/noxfile.py @@ -89,7 +89,7 @@ def get_pytest_env_vars() -> Dict[str, str]: # DO NOT EDIT - automatically generated. # All versions used to test samples. -ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10", "3.11"] +ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"] # Any default versions that should be ignored. IGNORED_VERSIONS = TEST_CONFIG["ignored_versions"] From 324bb24bb45c8fb09637b630f6ed39c933648a1a Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Tue, 12 Dec 2023 23:18:29 +0100 Subject: [PATCH 42/52] chore(deps): update dependency google-cloud-datastore to v2.19.0 (#508) --- datastore/samples/snippets/requirements.txt | 2 +- datastore/samples/snippets/schedule-export/requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/datastore/samples/snippets/requirements.txt b/datastore/samples/snippets/requirements.txt index d4e90e37456..5bccacc5f61 100644 --- a/datastore/samples/snippets/requirements.txt +++ b/datastore/samples/snippets/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.18.0 \ No newline at end of file +google-cloud-datastore==2.19.0 \ No newline at end of file diff --git a/datastore/samples/snippets/schedule-export/requirements.txt b/datastore/samples/snippets/schedule-export/requirements.txt index a84b83a1fe6..b748abdc9c1 100644 --- a/datastore/samples/snippets/schedule-export/requirements.txt +++ b/datastore/samples/snippets/schedule-export/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.18.0 +google-cloud-datastore==2.19.0 From 702775e28e0f1d15e3ce6fb91c6d7c53b5242209 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Mon, 22 Jan 2024 20:40:58 +0100 Subject: [PATCH 43/52] chore(deps): update dependency pytest to v7.4.4 (#511) --- datastore/samples/snippets/requirements-test.txt | 2 +- .../samples/snippets/schedule-export/requirements-test.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/datastore/samples/snippets/requirements-test.txt b/datastore/samples/snippets/requirements-test.txt index dfba3f35d90..80d3b1a9ebe 100644 --- a/datastore/samples/snippets/requirements-test.txt +++ b/datastore/samples/snippets/requirements-test.txt @@ -1,4 +1,4 @@ backoff===1.11.1; python_version < "3.7" backoff==2.2.1; python_version >= "3.7" -pytest==7.4.3 +pytest==7.4.4 flaky==3.7.0 diff --git a/datastore/samples/snippets/schedule-export/requirements-test.txt b/datastore/samples/snippets/schedule-export/requirements-test.txt index f16ee69ae92..fa427e19002 100644 --- a/datastore/samples/snippets/schedule-export/requirements-test.txt +++ b/datastore/samples/snippets/schedule-export/requirements-test.txt @@ -1 +1 @@ -pytest==7.4.3 \ No newline at end of file +pytest==7.4.4 \ No newline at end of file From dc7a4adb22bea54e8f24485cc6007b669e3f47e2 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Wed, 7 Aug 2024 14:57:47 -0600 Subject: [PATCH 44/52] feat: implement query profiling (#542) --- datastore/samples/snippets/snippets.py | 110 ++++++++++++++++++-- datastore/samples/snippets/snippets_test.py | 72 +++++++++---- 2 files changed, 155 insertions(+), 27 deletions(-) diff --git a/datastore/samples/snippets/snippets.py b/datastore/samples/snippets/snippets.py index 749a1ce4d74..1b86ba8b0cd 100644 --- a/datastore/samples/snippets/snippets.py +++ b/datastore/samples/snippets/snippets.py @@ -278,15 +278,16 @@ def sum_query_property_filter(client): # Execute sum aggregation query with filters completed_tasks = client.query(kind="Task").add_filter("done", "=", True) completed_tasks_query = client.aggregation_query(query=completed_tasks).sum( - property_ref="hours", - alias="total_completed_sum_hours" + property_ref="hours", alias="total_completed_sum_hours" ) completed_query_result = completed_tasks_query.fetch() for aggregation_results in completed_query_result: for aggregation_result in aggregation_results: if aggregation_result.alias == "total_completed_sum_hours": - print(f"Total sum of hours in completed tasks is {aggregation_result.value}") + print( + f"Total sum of hours in completed tasks is {aggregation_result.value}" + ) # [END datastore_sum_aggregation_query_with_filters] return tasks @@ -339,15 +340,16 @@ def avg_query_property_filter(client): # Execute average aggregation query with filters completed_tasks = client.query(kind="Task").add_filter("done", "=", True) completed_tasks_query = client.aggregation_query(query=completed_tasks).avg( - property_ref="hours", - alias="total_completed_avg_hours" + property_ref="hours", alias="total_completed_avg_hours" ) completed_query_result = completed_tasks_query.fetch() for aggregation_results in completed_query_result: for aggregation_result in aggregation_results: if aggregation_result.alias == "total_completed_avg_hours": - print(f"Total average of hours in completed tasks is {aggregation_result.value}") + print( + f"Total average of hours in completed tasks is {aggregation_result.value}" + ) # [END datastore_avg_aggregation_query_with_filters] return tasks @@ -375,9 +377,11 @@ def multiple_aggregations_query(client): [ datastore.aggregation.CountAggregation(alias="count_aggregation"), datastore.aggregation.SumAggregation( - property_ref="hours", alias="sum_aggregation"), + property_ref="hours", alias="sum_aggregation" + ), datastore.aggregation.AvgAggregation( - property_ref="hours", alias="avg_aggregation") + property_ref="hours", alias="avg_aggregation" + ), ] ) @@ -389,6 +393,96 @@ def multiple_aggregations_query(client): return tasks +def explain_analyze_entity(client): + # [START datastore_query_explain_analyze_entity] + # Build the query with explain_options + # analzye = true to get back the query stats, plan info, and query results + query = client.query( + kind="Task", explain_options=datastore.ExplainOptions(analyze=True) + ) + + # initiate the query + iterator = query.fetch() + + # explain_metrics is only available after query is completed + for task_result in iterator: + print(task_result) + + # get the plan summary + plan_summary = iterator.explain_metrics.plan_summary + print(f"Indexes used: {plan_summary.indexes_used}") + + # get the execution stats + execution_stats = iterator.explain_metrics.execution_stats + print(f"Results returned: {execution_stats.results_returned}") + print(f"Execution duration: {execution_stats.execution_duration}") + print(f"Read operations: {execution_stats.read_operations}") + print(f"Debug stats: {execution_stats.debug_stats}") + # [END datastore_query_explain_analyze_entity] + + +def explain_entity(client): + # [START datastore_query_explain_entity] + # Build the query with explain_options + # by default (analyze = false), only plan_summary property is available + query = client.query(kind="Task", explain_options=datastore.ExplainOptions()) + + # initiate the query + iterator = query.fetch() + + # get the plan summary + plan_summary = iterator.explain_metrics.plan_summary + print(f"Indexes used: {plan_summary.indexes_used}") + # [END datastore_query_explain_entity] + + +def explain_analyze_aggregation(client): + # [START datastore_query_explain_analyze_aggregation] + # Build the aggregation query with explain_options + # analzye = true to get back the query stats, plan info, and query results + all_tasks_query = client.query(kind="Task") + count_query = client.aggregation_query( + all_tasks_query, explain_options=datastore.ExplainOptions(analyze=True) + ).count() + + # initiate the query + iterator = count_query.fetch() + + # explain_metrics is only available after query is completed + for task_result in iterator: + print(task_result) + + # get the plan summary + plan_summary = iterator.explain_metrics.plan_summary + print(f"Indexes used: {plan_summary.indexes_used}") + + # get the execution stats + execution_stats = iterator.explain_metrics.execution_stats + print(f"Results returned: {execution_stats.results_returned}") + print(f"Execution duration: {execution_stats.execution_duration}") + print(f"Read operations: {execution_stats.read_operations}") + print(f"Debug stats: {execution_stats.debug_stats}") + # [END datastore_query_explain_analyze_aggregation] + + +def explain_aggregation(client): + # [START datastore_query_explain_aggregation] + # Build the aggregation query with explain_options + # by default (analyze = false), only plan_summary property is available + all_tasks_query = client.query(kind="Task") + count_query = client.aggregation_query( + all_tasks_query, explain_options=datastore.ExplainOptions() + ).count() + + # initiate the query + iterator = count_query.fetch() + + # get the plan summary + plan_summary = iterator.explain_metrics.plan_summary + print(f"Indexes used: {plan_summary.indexes_used}") + # [END datastore_query_explain_aggregation] + + def main(project_id): client = datastore.Client(project_id) diff --git a/datastore/samples/snippets/snippets_test.py b/datastore/samples/snippets/snippets_test.py index 92db05075d9..ae3b2948b34 100644 --- a/datastore/samples/snippets/snippets_test.py +++ b/datastore/samples/snippets/snippets_test.py @@ -52,17 +52,15 @@ def setup_indexes(request): indexes = [] done_property_index = datastore_admin_v1.Index.IndexedProperty( - name='done', - direction=datastore_admin_v1.Index.Direction.ASCENDING + name="done", direction=datastore_admin_v1.Index.Direction.ASCENDING ) hour_property_index = datastore_admin_v1.Index.IndexedProperty( - name='hours', - direction=datastore_admin_v1.Index.Direction.ASCENDING + name="hours", direction=datastore_admin_v1.Index.Direction.ASCENDING ) done_hour_index = datastore_admin_v1.Index( - kind='Task', + kind="Task", ancestor=datastore_admin_v1.Index.AncestorMode.NONE, - properties=[done_property_index, hour_property_index] + properties=[done_property_index, hour_property_index], ) indexes.append(done_hour_index) @@ -157,9 +155,7 @@ def test_count_query_with_stale_read(self, capsys, client): def test_sum_query_on_kind(self, capsys, client): tasks = snippets.sum_query_on_kind(client) captured = capsys.readouterr() - assert ( - captured.out.strip() == "Total sum of hours in tasks is 9" - ) + assert captured.out.strip() == "Total sum of hours in tasks is 9" assert captured.err == "" client.entities_to_delete.extend(tasks) @@ -168,9 +164,7 @@ def test_sum_query_on_kind(self, capsys, client): def test_sum_query_property_filter(self, capsys, client): tasks = snippets.sum_query_property_filter(client) captured = capsys.readouterr() - assert ( - captured.out.strip() == "Total sum of hours in completed tasks is 8" - ) + assert captured.out.strip() == "Total sum of hours in completed tasks is 8" assert captured.err == "" client.entities_to_delete.extend(tasks) @@ -179,9 +173,7 @@ def test_sum_query_property_filter(self, capsys, client): def test_avg_query_on_kind(self, capsys, client): tasks = snippets.avg_query_on_kind(client) captured = capsys.readouterr() - assert ( - captured.out.strip() == "Total average of hours in tasks is 3.0" - ) + assert captured.out.strip() == "Total average of hours in tasks is 3.0" assert captured.err == "" client.entities_to_delete.extend(tasks) @@ -201,15 +193,57 @@ def test_avg_query_property_filter(self, capsys, client): def test_multiple_aggregations_query(self, capsys, client): tasks = snippets.multiple_aggregations_query(client) captured = capsys.readouterr() + assert "avg_aggregation value is 3.0" in captured.out + assert "count_aggregation value is 3" in captured.out + assert "sum_aggregation value is 9" in captured.out + assert captured.err == "" + + client.entities_to_delete.extend(tasks) + + @backoff.on_exception(backoff.expo, AssertionError, max_time=240) + def test_explain_analyze_entity(self, capsys, client): + snippets.explain_analyze_entity(client) + captured = capsys.readouterr() assert ( - 'avg_aggregation value is 3.0' in captured.out + "Indexes used: [{'properties': '(__name__ ASC)', 'query_scope': 'Collection group'}]" + in captured.out ) + assert "Results returned: 0" in captured.out + assert "Execution duration: 0:00" in captured.out + assert "Read operations: 0" in captured.out + assert "Debug stats: {" in captured.out + assert captured.err == "" + + @backoff.on_exception(backoff.expo, AssertionError, max_time=240) + def test_explain_entity(self, capsys, client): + snippets.explain_entity(client) + captured = capsys.readouterr() assert ( - 'count_aggregation value is 3' in captured.out + "Indexes used: [{'properties': '(__name__ ASC)', 'query_scope': 'Collection group'}]" + in captured.out ) + assert captured.err == "" + + @backoff.on_exception(backoff.expo, AssertionError, max_time=240) + def test_explain_analyze_aggregation(self, capsys, client): + snippets.explain_analyze_aggregation(client) + captured = capsys.readouterr() assert ( - 'sum_aggregation value is 9' in captured.out + "Indexes used: [{'properties': '(__name__ ASC)', 'query_scope': 'Collection group'}]" + in captured.out ) + assert "Results returned: 1" in captured.out + assert "Execution duration: 0:00" in captured.out + assert "Read operations: 1" in captured.out + assert "Debug stats: {" in captured.out assert captured.err == "" - client.entities_to_delete.extend(tasks) + @backoff.on_exception(backoff.expo, AssertionError, max_time=240) + def test_explain_aggregation(self, capsys, client): + snippets.explain_aggregation(client) + captured = capsys.readouterr() + assert ( + "Indexes used: [{'properties': '(__name__ ASC)', 'query_scope': 'Collection group'}]" + in captured.out + ) + assert captured.err == "" From b705ba1f23c385c8c164bb836c2fbc58f92a551d Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Wed, 14 Aug 2024 18:56:42 +0200 Subject: [PATCH 45/52] chore(deps): update all dependencies (#519) * chore(deps): update all dependencies * pin pytest for python 3.7 --------- Co-authored-by: Daniel Sanche Co-authored-by: Anthonios Partheniou --- datastore/samples/snippets/requirements-test.txt | 5 +++-- .../samples/snippets/schedule-export/requirements-test.txt | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/datastore/samples/snippets/requirements-test.txt b/datastore/samples/snippets/requirements-test.txt index 80d3b1a9ebe..81c08fe2c07 100644 --- a/datastore/samples/snippets/requirements-test.txt +++ b/datastore/samples/snippets/requirements-test.txt @@ -1,4 +1,5 @@ backoff===1.11.1; python_version < "3.7" backoff==2.2.1; python_version >= "3.7" -pytest==7.4.4 -flaky==3.7.0 +pytest===7.4.3; python_version == '3.7' +pytest==8.3.2; python_version >= '3.8' +flaky==3.8.1 diff --git a/datastore/samples/snippets/schedule-export/requirements-test.txt b/datastore/samples/snippets/schedule-export/requirements-test.txt index fa427e19002..6c461db6f4c 100644 --- a/datastore/samples/snippets/schedule-export/requirements-test.txt +++ b/datastore/samples/snippets/schedule-export/requirements-test.txt @@ -1 +1,2 @@ -pytest==7.4.4 \ No newline at end of file +pytest===7.4.3; python_version == '3.7' +pytest==8.3.2; python_version >= '3.8' \ No newline at end of file From 9840d5d78031822cab6641c9ddc38a0fab7fee81 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Thu, 19 Sep 2024 22:02:42 +0200 Subject: [PATCH 46/52] chore(deps): update all dependencies (#563) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore(deps): update all dependencies * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --------- Co-authored-by: Owl Bot Co-authored-by: Anthonios Partheniou --- datastore/samples/snippets/requirements.txt | 2 +- datastore/samples/snippets/schedule-export/requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/datastore/samples/snippets/requirements.txt b/datastore/samples/snippets/requirements.txt index 5bccacc5f61..8816d485f0f 100644 --- a/datastore/samples/snippets/requirements.txt +++ b/datastore/samples/snippets/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.19.0 \ No newline at end of file +google-cloud-datastore==2.20.0 \ No newline at end of file diff --git a/datastore/samples/snippets/schedule-export/requirements.txt b/datastore/samples/snippets/schedule-export/requirements.txt index b748abdc9c1..7f50395730c 100644 --- a/datastore/samples/snippets/schedule-export/requirements.txt +++ b/datastore/samples/snippets/schedule-export/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.19.0 +google-cloud-datastore==2.20.0 From 5ee3fdb02a4d00385a87931f5078951334aef8f8 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Fri, 15 Nov 2024 13:23:46 -0800 Subject: [PATCH 47/52] chore(python): update dependencies in .kokoro/docker/docs (#574) * chore(python): update dependencies in .kokoro/docker/docs Source-Link: https://github.com/googleapis/synthtool/commit/59171c8f83f3522ce186e4d110d27e772da4ba7a Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-python:latest@sha256:2ed982f884312e4883e01b5ab8af8b6935f0216a5a2d82928d273081fc3be562 * Add constraints file for python 3.13 --------- Co-authored-by: Owl Bot Co-authored-by: Anthonios Partheniou --- datastore/samples/snippets/noxfile.py | 2 +- datastore/samples/snippets/schedule-export/noxfile.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/datastore/samples/snippets/noxfile.py b/datastore/samples/snippets/noxfile.py index 483b5590179..a169b5b5b46 100644 --- a/datastore/samples/snippets/noxfile.py +++ b/datastore/samples/snippets/noxfile.py @@ -89,7 +89,7 @@ def get_pytest_env_vars() -> Dict[str, str]: # DO NOT EDIT - automatically generated. # All versions used to test samples. -ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"] +ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] # Any default versions that should be ignored. IGNORED_VERSIONS = TEST_CONFIG["ignored_versions"] diff --git a/datastore/samples/snippets/schedule-export/noxfile.py b/datastore/samples/snippets/schedule-export/noxfile.py index 483b5590179..a169b5b5b46 100644 --- a/datastore/samples/snippets/schedule-export/noxfile.py +++ b/datastore/samples/snippets/schedule-export/noxfile.py @@ -89,7 +89,7 @@ def get_pytest_env_vars() -> Dict[str, str]: # DO NOT EDIT - automatically generated. # All versions used to test samples. -ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"] +ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] # Any default versions that should be ignored. IGNORED_VERSIONS = TEST_CONFIG["ignored_versions"] From 1ad71d8c9d0a3d6af77add9fb657374989bbe7bd Mon Sep 17 00:00:00 2001 From: Chalmer Lowe Date: Tue, 11 Nov 2025 07:17:45 -0500 Subject: [PATCH 48/52] chore(python): Add support for Python 3.14 (#644) This PR adds support for Python 3.14 to the library. Key changes include: - Update to `.github/workflows` files to account for Python runtimes both 3.14 and 3.8. - Adding Python 3.14 to the test matrix in `.github/workflows/unittest.yml`, etc. - Updating `.github/sync-repo-settings.yaml` to include 3.14 unit tests in required checks. - Update to `.kokoro/presubmit` files to update the system test and `presubmit.cfg` - Adding `testing/constraints-3.14.txt`. - Updates `CONTRIBUTING.rst` to list Python 3.14 as a supported version. - Updates `mypy.ini` to suppress several known type hinting errors in two files. - Updating `noxfile.py` to include 3.14 sessions. - Updates to `owlbot.py` to include Python 3.14. - Updating `setup.py` to include the Python 3.14 classifier and add conditional dependencies for `grpcio`. --------- Co-authored-by: Owl Bot Co-authored-by: Anthonios Partheniou --- datastore/samples/snippets/noxfile.py | 2 +- datastore/samples/snippets/schedule-export/noxfile.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/datastore/samples/snippets/noxfile.py b/datastore/samples/snippets/noxfile.py index a169b5b5b46..69bcaf56de6 100644 --- a/datastore/samples/snippets/noxfile.py +++ b/datastore/samples/snippets/noxfile.py @@ -89,7 +89,7 @@ def get_pytest_env_vars() -> Dict[str, str]: # DO NOT EDIT - automatically generated. # All versions used to test samples. -ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] +ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] # Any default versions that should be ignored. IGNORED_VERSIONS = TEST_CONFIG["ignored_versions"] diff --git a/datastore/samples/snippets/schedule-export/noxfile.py b/datastore/samples/snippets/schedule-export/noxfile.py index a169b5b5b46..69bcaf56de6 100644 --- a/datastore/samples/snippets/schedule-export/noxfile.py +++ b/datastore/samples/snippets/schedule-export/noxfile.py @@ -89,7 +89,7 @@ def get_pytest_env_vars() -> Dict[str, str]: # DO NOT EDIT - automatically generated. # All versions used to test samples. -ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] +ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] # Any default versions that should be ignored. IGNORED_VERSIONS = TEST_CONFIG["ignored_versions"] From 9a4720d82a8d8b1e754424e025518e85ba90b153 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Fri, 13 Feb 2026 16:37:43 +0000 Subject: [PATCH 49/52] chore(deps): update all dependencies (#660) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit > **Note:** This PR body was truncated due to platform limits. This PR contains the following updates: | Package | Type | Update | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | |---|---|---|---|---|---| | [actions/checkout](https://redirect.github.com/actions/checkout) | action | major | `v4` → `v6` | ![age](https://developer.mend.io/api/mc/badges/age/github-tags/actions%2fcheckout/v6?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/github-tags/actions%2fcheckout/v4/v6?slim=true) | | [actions/setup-python](https://redirect.github.com/actions/setup-python) | action | major | `v5` → `v6` | ![age](https://developer.mend.io/api/mc/badges/age/github-tags/actions%2fsetup-python/v6?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/github-tags/actions%2fsetup-python/v5/v6?slim=true) | | [google-cloud-datastore](https://redirect.github.com/googleapis/python-datastore) | | minor | `==2.20.0` → `==2.23.0` | ![age](https://developer.mend.io/api/mc/badges/age/pypi/google-cloud-datastore/2.23.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/google-cloud-datastore/2.20.0/2.23.0?slim=true) | | [pytest](https://redirect.github.com/pytest-dev/pytest) ([changelog](https://docs.pytest.org/en/stable/changelog.html)) | | major | `==8.3.2` → `==9.0.2` | ![age](https://developer.mend.io/api/mc/badges/age/pypi/pytest/9.0.2?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/pytest/8.3.2/9.0.2?slim=true) | | [python](https://redirect.github.com/actions/python-versions) | uses-with | minor | `3.10` → `3.14` | ![age](https://developer.mend.io/api/mc/badges/age/github-releases/actions%2fpython-versions/3.14.2?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/github-releases/actions%2fpython-versions/3.10.19/3.14.2?slim=true) | --- ### Release Notes
actions/checkout (actions/checkout) ### [`v6`](https://redirect.github.com/actions/checkout/compare/v5...v6) [Compare Source](https://redirect.github.com/actions/checkout/compare/v5...v6) ### [`v5`](https://redirect.github.com/actions/checkout/compare/v4...v5) [Compare Source](https://redirect.github.com/actions/checkout/compare/v4...v5)
actions/setup-python (actions/setup-python) ### [`v6`](https://redirect.github.com/actions/setup-python/compare/v5...v6) [Compare Source](https://redirect.github.com/actions/setup-python/compare/v5...v6)
googleapis/python-datastore (google-cloud-datastore) ### [`v2.23.0`](https://redirect.github.com/googleapis/python-datastore/blob/HEAD/CHANGELOG.md#2230-2025-12-16) [Compare Source](https://redirect.github.com/googleapis/python-datastore/compare/v2.22.0...v2.23.0) ##### Features - support mTLS certificates when available ([#​658](https://redirect.github.com/googleapis/python-datastore/issues/658)) ([85c023287daebb0d5c1a009e2beaccf0c6ea75eb](https://redirect.github.com/googleapis/python-datastore/commit/85c023287daebb0d5c1a009e2beaccf0c6ea75eb)) ### [`v2.22.0`](https://redirect.github.com/googleapis/python-datastore/blob/HEAD/CHANGELOG.md#2220-2025-12-12) [Compare Source](https://redirect.github.com/googleapis/python-datastore/compare/v2.21.0...v2.22.0) ##### Bug Fixes - remove setup.cfg configuration for creating universal wheels ([#​601](https://redirect.github.com/googleapis/python-datastore/issues/601)) ([df729015149bd69e9d6dbced260d97c8eed77d4f](https://redirect.github.com/googleapis/google-cloud-python/commit/df729015149bd69e9d6dbced260d97c8eed77d4f)) ### [`v2.21.0`](https://redirect.github.com/googleapis/python-datastore/blob/HEAD/CHANGELOG.md#2210-2025-04-10) [Compare Source](https://redirect.github.com/googleapis/python-datastore/compare/v2.20.2...v2.21.0) ##### Features - Add REST Interceptors which support reading metadata ([7be9c4c](https://redirect.github.com/googleapis/python-datastore/commit/7be9c4c594af2c2414e394b8bfe62574b58ef337)) - Add support for opt-in debug logging ([7be9c4c](https://redirect.github.com/googleapis/python-datastore/commit/7be9c4c594af2c2414e394b8bfe62574b58ef337)) ##### Bug Fixes - Allow protobuf 6.x ([#​598](https://redirect.github.com/googleapis/python-datastore/issues/598)) ([7c1171b](https://redirect.github.com/googleapis/python-datastore/commit/7c1171bf657f7cf4d1404e19611f6c874a8998ca)) - Backwards-compatibility for previous meaning format ([#​603](https://redirect.github.com/googleapis/python-datastore/issues/603)) ([ed92e8e](https://redirect.github.com/googleapis/python-datastore/commit/ed92e8e54a9e0f44302efee89a30a322d0a73636)) - Fix typing issue with gRPC metadata when key ends in -bin ([7be9c4c](https://redirect.github.com/googleapis/python-datastore/commit/7be9c4c594af2c2414e394b8bfe62574b58ef337)) ### [`v2.20.2`](https://redirect.github.com/googleapis/python-datastore/blob/HEAD/CHANGELOG.md#2202-2024-12-12) [Compare Source](https://redirect.github.com/googleapis/python-datastore/compare/v2.20.1...v2.20.2) ##### Bug Fixes - Preserve list meanings ([#​575](https://redirect.github.com/googleapis/python-datastore/issues/575)) ([266243b](https://redirect.github.com/googleapis/python-datastore/commit/266243ba360a9d41ab4b51c323eac44d2cfc35cb)) ### [`v2.20.1`](https://redirect.github.com/googleapis/python-datastore/blob/HEAD/CHANGELOG.md#2201-2024-08-14) [Compare Source](https://redirect.github.com/googleapis/python-datastore/compare/v2.20.0...v2.20.1) ##### Bug Fixes - Allow protobuf 5.x; require protobuf >=3.20.2 ([#​560](https://redirect.github.com/googleapis/python-datastore/issues/560)) ([ad50e36](https://redirect.github.com/googleapis/python-datastore/commit/ad50e3648954edf27575001be833bb5e1e598f46))
pytest-dev/pytest (pytest) ### [`v9.0.2`](https://redirect.github.com/pytest-dev/pytest/releases/tag/9.0.2) [Compare Source](https://redirect.github.com/pytest-dev/pytest/compare/9.0.1...9.0.2) ### pytest 9.0.2 (2025-12-06) #### Bug fixes - [#​13896](https://redirect.github.com/pytest-dev/pytest/issues/13896): The terminal progress feature added in pytest 9.0.0 has been disabled by default, except on Windows, due to compatibility issues with some terminal emulators. You may enable it again by passing `-p terminalprogress`. We may enable it by default again once compatibility improves in the future. Additionally, when the environment variable `TERM` is `dumb`, the escape codes are no longer emitted, even if the plugin is enabled. - [#​13904](https://redirect.github.com/pytest-dev/pytest/issues/13904): Fixed the TOML type of the `tmp_path_retention_count` settings in the API reference from number to string. - [#​13946](https://redirect.github.com/pytest-dev/pytest/issues/13946): The private `config.inicfg` attribute was changed in a breaking manner in pytest 9.0.0. Due to its usage in the ecosystem, it is now restored to working order using a compatibility shim. It will be deprecated in pytest 9.1 and removed in pytest 10. - [#​13965](https://redirect.github.com/pytest-dev/pytest/issues/13965): Fixed quadratic-time behavior when handling `unittest` subtests in Python 3.10. #### Improved documentation - [#​4492](https://redirect.github.com/pytest-dev/pytest/issues/4492): The API Reference now contains cross-reference-able documentation of `pytest's command-line flags `. ### [`v9.0.1`](https://redirect.github.com/pytest-dev/pytest/releases/tag/9.0.1) [Compare Source](https://redirect.github.com/pytest-dev/pytest/compare/9.0.0...9.0.1) ### pytest 9.0.1 (2025-11-12) #### Bug fixes - [#​13895](https://redirect.github.com/pytest-dev/pytest/issues/13895): Restore support for skipping tests via `raise unittest.SkipTest`. - [#​13896](https://redirect.github.com/pytest-dev/pytest/issues/13896): The terminal progress plugin added in pytest 9.0 is now automatically disabled when iTerm2 is detected, it generated desktop notifications instead of the desired functionality. - [#​13904](https://redirect.github.com/pytest-dev/pytest/issues/13904): Fixed the TOML type of the verbosity settings in the API reference from number to string. - [#​13910](https://redirect.github.com/pytest-dev/pytest/issues/13910): Fixed UserWarning: Do not expect file\_or\_dir on some earlier Python 3.12 and 3.13 point versions. #### Packaging updates and notes for downstreams - [#​13933](https://redirect.github.com/pytest-dev/pytest/issues/13933): The tox configuration has been adjusted to make sure the desired version string can be passed into its `package_env` through the `SETUPTOOLS_SCM_PRETEND_VERSION_FOR_PYTEST` environment variable as a part of the release process -- by `webknjaz`. #### Contributor-facing changes - [#​13891](https://redirect.github.com/pytest-dev/pytest/issues/13891), [#​13942](https://redirect.github.com/pytest-dev/pytest/issues/13942): The CI/CD part of the release automation is now capable of creating GitHub Releases without having a Git checkout on disk -- by `bluetech` and `webknjaz`. - [#​13933](https://redirect.github.com/pytest-dev/pytest/issues/13933): The tox configuration has been adjusted to make sure the desired version string can be passed into its `package_env` through the `SETUPTOOLS_SCM_PRETEND_VERSION_FOR_PYTEST` environment variable as a part of the release process -- by `webknjaz`. ### [`v9.0.0`](https://redirect.github.com/pytest-dev/pytest/releases/tag/9.0.0) [Compare Source](https://redirect.github.com/pytest-dev/pytest/compare/8.4.2...9.0.0) ### pytest 9.0.0 (2025-11-05) #### New features - [#​1367](https://redirect.github.com/pytest-dev/pytest/issues/1367): **Support for subtests** has been added. `subtests ` are an alternative to parametrization, useful in situations where the parametrization values are not all known at collection time. Example: ```python def contains_docstring(p: Path) -> bool: """Return True if the given Python file contains a top-level docstring.""" ... def test_py_files_contain_docstring(subtests: pytest.Subtests) -> None: for path in Path.cwd().glob("*.py"): with subtests.test(path=str(path)): assert contains_docstring(path) ``` Each assert failure or error is caught by the context manager and reported individually, giving a clear picture of all files that are missing a docstring. In addition, `unittest.TestCase.subTest` is now also supported. This feature was originally implemented as a separate plugin in [pytest-subtests](https://redirect.github.com/pytest-dev/pytest-subtests), but since then has been merged into the core. > \[!NOTE] > This feature is experimental and will likely evolve in future releases. By that we mean that we might change how subtests are reported on failure, but the functionality and how to use it are stable. - [#​13743](https://redirect.github.com/pytest-dev/pytest/issues/13743): Added support for **native TOML configuration files**. While pytest, since version 6, supports configuration in `pyproject.toml` files under `[tool.pytest.ini_options]`, it does so in an "INI compatibility mode", where all configuration values are treated as strings or list of strings. Now, pytest supports the native TOML data model. In `pyproject.toml`, the native TOML configuration is under the `[tool.pytest]` table. ```toml # pyproject.toml [tool.pytest] minversion = "9.0" addopts = ["-ra", "-q"] testpaths = [ "tests", "integration", ] ``` The `[tool.pytest.ini_options]` table remains supported, but both tables cannot be used at the same time. If you prefer to use a separate configuration file, or don't use `pyproject.toml`, you can use `pytest.toml` or `.pytest.toml`: ```toml # pytest.toml or .pytest.toml [pytest] minversion = "9.0" addopts = ["-ra", "-q"] testpaths = [ "tests", "integration", ] ``` The documentation now (sometimes) shows configuration snippets in both TOML and INI formats, in a tabbed interface. See `config file formats` for full details. - [#​13823](https://redirect.github.com/pytest-dev/pytest/issues/13823): Added a **"strict mode"** enabled by the `strict` configuration option. When set to `true`, the `strict` option currently enables - `strict_config` - `strict_markers` - `strict_parametrization_ids` - `strict_xfail` The individual strictness options can be explicitly set to override the global `strict` setting. The previously-deprecated `--strict` command-line flag now enables strict mode. If pytest adds new strictness options in the future, they will also be enabled in strict mode. Therefore, you should only enable strict mode if you use a pinned/locked version of pytest, or if you want to proactively adopt new strictness options as they are added. See `strict mode` for more details. - [#​13737](https://redirect.github.com/pytest-dev/pytest/issues/13737): Added the `strict_parametrization_ids` configuration option. When set, pytest emits an error if it detects non-unique parameter set IDs, rather than automatically making the IDs unique by adding 0, 1, ... to them. This can be particularly useful for catching unintended duplicates. - [#​13072](https://redirect.github.com/pytest-dev/pytest/issues/13072): Added support for displaying test session **progress in the terminal tab** using the [OSC 9;4;](https://conemu.github.io/en/AnsiEscapeCodes.html#ConEmu_specific_OSC) ANSI sequence. When pytest runs in a supported terminal emulator like ConEmu, Gnome Terminal, Ptyxis, Windows Terminal, Kitty or Ghostty, you'll see the progress in the terminal tab or window, allowing you to monitor pytest's progress at a glance. This feature is automatically enabled when running in a TTY. It is implemented as an internal plugin. If needed, it can be disabled as follows: - On a user level, using `-p no:terminalprogress` on the command line or via an environment variable `PYTEST_ADDOPTS='-p no:terminalprogress'`. - On a project configuration level, using `addopts = "-p no:terminalprogress"`. - [#​478](https://redirect.github.com/pytest-dev/pytest/issues/478): Support PEP420 (implicit namespace packages) as --pyargs target when `consider_namespace_packages` is true in the config. Previously, this option only impacted package imports, now it also impacts tests discovery. - [#​13678](https://redirect.github.com/pytest-dev/pytest/issues/13678): Added a new `faulthandler_exit_on_timeout` configuration option set to "false" by default to let faulthandler interrupt the pytest process after a timeout in case of deadlock. Previously, a faulthandler timeout would only dump the traceback of all threads to stderr, but would not interrupt the pytest process. \-- by `ogrisel`. - [#​13829](https://redirect.github.com/pytest-dev/pytest/issues/13829): Added support for configuration option aliases via the `aliases` parameter in `Parser.addini() `. Plugins can now register alternative names for configuration options, allowing for more flexibility in configuration naming and supporting backward compatibility when renaming options. The canonical name always takes precedence if both the canonical name and an alias are specified in the configuration file. #### Improvements in existing functionality - [#​13330](https://redirect.github.com/pytest-dev/pytest/issues/13330): Having pytest configuration spread over more than one file (for example having both a `pytest.ini` file and `pyproject.toml` with a `[tool.pytest.ini_options]` table) will now print a warning to make it clearer to the user that only one of them is actually used. \-- by `sgaist` - [#​13574](https://redirect.github.com/pytest-dev/pytest/issues/13574): The single argument `--version` no longer loads the entire plugin infrastructure, making it faster and more reliable when displaying only the pytest version. Passing `--version` twice (e.g., `pytest --version --version`) retains the original behavior, showing both the pytest version and plugin information. > \[!NOTE] > Since `--version` is now processed early, it only takes effect when passed directly via the command line. It will not work if set through other mechanisms, such as `PYTEST_ADDOPTS` or `addopts`. - [#​13823](https://redirect.github.com/pytest-dev/pytest/issues/13823): Added `strict_xfail` as an alias to the `xfail_strict` option, `strict_config` as an alias to the `--strict-config` flag, and `strict_markers` as an alias to the `--strict-markers` flag. This makes all strictness options consistently have configuration options with the prefix `strict_`. - [#​13700](https://redirect.github.com/pytest-dev/pytest/issues/13700): --junitxml no longer prints the generated xml file summary at the end of the pytest session when --quiet is given. - [#​13732](https://redirect.github.com/pytest-dev/pytest/issues/13732): Previously, when filtering warnings, pytest would fail if the filter referenced a class that could not be imported. Now, this only outputs a message indicating the problem. - [#​13859](https://redirect.github.com/pytest-dev/pytest/issues/13859): Clarify the error message for pytest.raises() when a regex match fails. - [#​13861](https://redirect.github.com/pytest-dev/pytest/issues/13861): Better sentence structure in a test's expected error message. Previously, the error message would be "expected exception must be \, but got \". Now, it is "Expected \, but got \". #### Removals and backward incompatible breaking changes - [#​12083](https://redirect.github.com/pytest-dev/pytest/issues/12083): Fixed a bug where an invocation such as pytest a/ a/b would cause only tests from a/b to run, and not other tests under a/. The fix entails a few breaking changes to how such overlapping arguments and duplicates are handled: 1. pytest a/b a/ or pytest a/ a/b are equivalent to pytest a; if an argument overlaps another arguments, only the prefix remains. 2. pytest x.py x.py is equivalent to pytest x.py; previously such an invocation was taken as an explicit request to run the tests from the file twice. If you rely on these behaviors, consider using `--keep-duplicates `, which retains its existing behavior (including the bug). - [#​13719](https://redirect.github.com/pytest-dev/pytest/issues/13719): Support for Python 3.9 is dropped following its end of life. - [#​13766](https://redirect.github.com/pytest-dev/pytest/issues/13766): Previously, pytest would assume it was running in a CI/CD environment if either of the environment variables $CI or $BUILD\_NUMBER was defined; now, CI mode is only activated if at least one of those variables is defined and set to a *non-empty* value. - [#​13779](https://redirect.github.com/pytest-dev/pytest/issues/13779): **PytestRemovedIn9Warning deprecation warnings are now errors by default.** Following our plan to remove deprecated features with as little disruption as possible, all warnings of type `PytestRemovedIn9Warning` now generate errors instead of warning messages by default. **The affected features will be effectively removed in pytest 9.1**, so please consult the `deprecations` section in the docs for directions on how to update existing code. In the pytest `9.0.X` series, it is possible to change the errors back into warnings as a stopgap measure by adding this to your `pytest.ini` file: ```ini [pytest] filterwarnings = ignore::pytest.PytestRemovedIn9Warning ``` But this will stop working when pytest `9.1` is released. **If you have concerns** about the removal of a specific feature, please add a comment to `13779`. #### Deprecations (removal in next major release) - [#​13807](https://redirect.github.com/pytest-dev/pytest/issues/13807): `monkeypatch.syspath_prepend() ` now issues a deprecation warning when the prepended path contains legacy namespace packages (those using `pkg_resources.declare_namespace()`). Users should migrate to native namespace packages (`420`). See `monkeypatch-fixup-namespace-packages` for details. #### Bug fixes - [#​13445](https://redirect.github.com/pytest-dev/pytest/issues/13445): Made the type annotations of `pytest.skip` and friends more spec-complaint to have them work across more type checkers. - [#​13537](https://redirect.github.com/pytest-dev/pytest/issues/13537): Fixed a bug in which `ExceptionGroup` with only `Skipped` exceptions in teardown was not handled correctly and showed as error. - [#​13598](https://redirect.github.com/pytest-dev/pytest/issues/13598): Fixed possible collection confusion on Windows when short paths and symlinks are involved. - [#​13716](https://redirect.github.com/pytest-dev/pytest/issues/13716): Fixed a bug where a nonsensical invocation like `pytest x.py[a]` (a file cannot be parametrized) was silently treated as `pytest x.py`. This is now a usage error. - [#​13722](https://redirect.github.com/pytest-dev/pytest/issues/13722): Fixed a misleading assertion failure message when using `pytest.approx` on mappings with differing lengths. - [#​13773](https://redirect.github.com/pytest-dev/pytest/issues/13773): Fixed the static fixture closure calculation to properly consider transitive dependencies requested by overridden fixtures. - [#​13816](https://redirect.github.com/pytest-dev/pytest/issues/13816): Fixed `pytest.approx` which now returns a clearer error message when comparing mappings with different keys. - [#​13849](https://redirect.github.com/pytest-dev/pytest/issues/13849): Hidden `.pytest.ini` files are now picked up as the config file even if empty. This was an inconsistency with non-hidden `pytest.ini`. - [#​13865](https://redirect.github.com/pytest-dev/pytest/issues/13865): Fixed --show-capture with --tb=line. - [#​13522](https://redirect.github.com/pytest-dev/pytest/issues/13522): Fixed `pytester` in subprocess mode ignored all :attr\`pytester.plugins \\` except the first. Fixed `pytester` in subprocess mode silently ignored non-str `pytester.plugins `. Now it errors instead. If you are affected by this, specify the plugin by name, or switch the affected tests to use `pytester.runpytest_inprocess ` explicitly instead. #### Packaging updates and notes for downstreams - [#​13791](https://redirect.github.com/pytest-dev/pytest/issues/13791): Minimum requirements on `iniconfig` and `packaging` were bumped to `1.0.1` and `22.0.0`, respectively. #### Contributor-facing changes - [#​12244](https://redirect.github.com/pytest-dev/pytest/issues/12244): Fixed self-test failures when TERM=dumb. - [#​12474](https://redirect.github.com/pytest-dev/pytest/issues/12474): Added scheduled GitHub Action Workflow to run Sphinx linkchecks in repo documentation. - [#​13621](https://redirect.github.com/pytest-dev/pytest/issues/13621): pytest's own testsuite now handles the `lsof` command hanging (e.g. due to unreachable network filesystems), with the affected selftests being skipped after 10 seconds. - [#​13638](https://redirect.github.com/pytest-dev/pytest/issues/13638): Fixed deprecated `gh pr new` command in `scripts/prepare-release-pr.py`. The script now uses `gh pr create` which is compatible with GitHub CLI v2.0+. - [#​13695](https://redirect.github.com/pytest-dev/pytest/issues/13695): Flush stdout and stderr in Pytester.run to avoid truncated outputs in test\_faulthandler.py::test\_timeout on CI -- by `ogrisel`. - [#​13771](https://redirect.github.com/pytest-dev/pytest/issues/13771): Skip test\_do\_not\_collect\_symlink\_siblings on Windows environments without symlink support to avoid false negatives. - [#​13841](https://redirect.github.com/pytest-dev/pytest/issues/13841): `tox>=4` is now required when contributing to pytest. - [#​13625](https://redirect.github.com/pytest-dev/pytest/issues/13625): Added missing docstrings to `pytest_addoption()`, `pytest_configure()`, and `cacheshow()` functions in `cacheprovider.py`. #### Miscellaneous internal changes - [#​13830](https://redirect.github.com/pytest-dev/pytest/issues/13830): Configuration overrides (`-o`/`--override-ini`) are now processed during startup rather than during `config.getini() `. ### [`v8.4.2`](https://redirect.github.com/pytest-dev/pytest/releases/tag/8.4.2) [Compare Source](https://redirect.github.com/pytest-dev/pytest/compare/8.4.1...8.4.2) ### pytest 8.4.2 (2025-09-03) #### Bug fixes - [#​13478](https://redirect.github.com/pytest-dev/pytest/issues/13478): Fixed a crash when using `console_output_style`{.interpreted-text role="confval"} with `times` and a module is skipped. - [#​13530](https://redirect.github.com/pytest-dev/pytest/issues/13530): Fixed a crash when using `pytest.approx`{.interpreted-text role="func"} and `decimal.Decimal`{.interpreted-text role="class"} instances with the `decimal.FloatOperation`{.interpreted-text role="class"} trap set. - [#​13549](https://redirect.github.com/pytest-dev/pytest/issues/13549): No longer evaluate type annotations in Python `3.14` when inspecting function signatures. This prevents crashes during module collection when modules do not explicitly use `from __future__ import annotations` and import types for annotations within a `if TYPE_CHECKING:` block. - [#​13559](https://redirect.github.com/pytest-dev/pytest/issues/13559): Added missing \[int]{.title-ref} and \[float]{.title-ref} variants to the \[Literal]{.title-ref} type annotation of the \[type]{.title-ref} parameter in `pytest.Parser.addini`{.interpreted-text role="meth"}. - [#​13563](https://redirect.github.com/pytest-dev/pytest/issues/13563): `pytest.approx`{.interpreted-text role="func"} now only imports `numpy` if NumPy is already in `sys.modules`. This fixes unconditional import behavior introduced in \[8.4.0]{.title-ref}. #### Improved documentation - [#​13577](https://redirect.github.com/pytest-dev/pytest/issues/13577): Clarify that `pytest_generate_tests` is discovered in test modules/classes; other hooks must be in `conftest.py` or plugins. #### Contributor-facing changes - [#​13480](https://redirect.github.com/pytest-dev/pytest/issues/13480): Self-testing: fixed a few test failures when run with `-Wdefault` or a similar override. - [#​13547](https://redirect.github.com/pytest-dev/pytest/issues/13547): Self-testing: corrected expected message for `test_doctest_unexpected_exception` in Python `3.14`. - [#​13684](https://redirect.github.com/pytest-dev/pytest/issues/13684): Make pytest's own testsuite insensitive to the presence of the `CI` environment variable -- by `ogrisel`{.interpreted-text role="user"}. ### [`v8.4.1`](https://redirect.github.com/pytest-dev/pytest/releases/tag/8.4.1) [Compare Source](https://redirect.github.com/pytest-dev/pytest/compare/8.4.0...8.4.1) ### pytest 8.4.1 (2025-06-17) #### Bug fixes - [#​13461](https://redirect.github.com/pytest-dev/pytest/issues/13461): Corrected `_pytest.terminal.TerminalReporter.isatty` to support being called as a method. Before it was just a boolean which could break correct code when using `-o log_cli=true`). - [#​13477](https://redirect.github.com/pytest-dev/pytest/issues/13477): Reintroduced `pytest.PytestReturnNotNoneWarning`{.interpreted-text role="class"} which was removed by accident in pytest \[8.4]{.title-ref}. This warning is raised when a test functions returns a value other than `None`, which is often a mistake made by beginners. See `return-not-none`{.interpreted-text role="ref"} for more information. - [#​13497](https://redirect.github.com/pytest-dev/pytest/issues/13497): Fixed compatibility with `Twisted 25+`. #### Improved documentation - [#​13492](https://redirect.github.com/pytest-dev/pytest/issues/13492): Fixed outdated warning about `faulthandler` not working on Windows. ### [`v8.4.0`](https://redirect.github.com/pytest-dev/pytest/releases/tag/8.4.0) [Compare Source](https://redirect.github.com/pytest-dev/pytest/compare/8.3.5...8.4.0) ### pytest 8.4.0 (2025-06-02) #### Removals and backward incompatible breaking changes - [#​11372](https://redirect.github.com/pytest-dev/pytest/issues/11372): Async tests will now fail, instead of warning+skipping, if you don't have any suitable plugin installed. - [#​12346](https://redirect.github.com/pytest-dev/pytest/issues/12346): Tests will now fail, instead of raising a warning, if they return any value other than None. - [#​12874](https://redirect.github.com/pytest-dev/pytest/issues/12874): We dropped support for Python 3.8 following its end of life (2024-10-07). - [#​12960](https://redirect.github.com/pytest-dev/pytest/issues/12960): Test functions containing a yield now cause an explicit error. They have not been run since pytest 4.0, and were previously marked as an expected failure and deprecation warning. See `the docs `{.interpreted-text role="ref"} for more information. #### Deprecations (removal in next major release) - [#​10839](https://redirect.github.com/pytest-dev/pytest/issues/10839): Requesting an asynchronous fixture without a \[pytest\_fixture\_setup]{.title-ref} hook that resolves it will now give a DeprecationWarning. This most commonly happens if a sync test requests an async fixture. This should have no effect on a majority of users with async tests or fixtures using async pytest plugins, but may affect non-standard hook setups or `autouse=True`. For guidance on how to work around this warning see `sync-test-async-fixture`{.interpreted-text role="ref"}. #### New features - [#​11538](https://redirect.github.com/pytest-dev/pytest/issues/11538): Added `pytest.RaisesGroup`{.interpreted-text role="class"} as an equivalent to `pytest.raises`{.interpreted-text role="func"} for expecting `ExceptionGroup`{.interpreted-text role="exc"}. Also adds `pytest.RaisesExc`{.interpreted-text role="class"} which is now the logic behind `pytest.raises`{.interpreted-text role="func"} and used as parameter to `pytest.RaisesGroup`{.interpreted-text role="class"}. `RaisesGroup` includes the ability to specify multiple different expected exceptions, the structure of nested exception groups, and flags for emulating `except* `{.interpreted-text role="ref"}. See `assert-matching-exception-groups`{.interpreted-text role="ref"} and docstrings for more information. - [#​12081](https://redirect.github.com/pytest-dev/pytest/issues/12081): Added `capteesys`{.interpreted-text role="fixture"} to capture AND pass output to next handler set by `--capture=`. - [#​12504](https://redirect.github.com/pytest-dev/pytest/issues/12504): `pytest.mark.xfail`{.interpreted-text role="func"} now accepts `pytest.RaisesGroup`{.interpreted-text role="class"} for the `raises` parameter when you expect an exception group. You can also pass a `pytest.RaisesExc`{.interpreted-text role="class"} if you e.g. want to make use of the `check` parameter. - [#​12713](https://redirect.github.com/pytest-dev/pytest/issues/12713): New \[--force-short-summary]{.title-ref} option to force condensed summary output regardless of verbosity level. This lets users still see condensed summary output of failures for quick reference in log files from job outputs, being especially useful if non-condensed output is very verbose. - [#​12749](https://redirect.github.com/pytest-dev/pytest/issues/12749): pytest traditionally collects classes/functions in the test module namespace even if they are imported from another file. For example: ```python ``` ### contents of src/domain.py ``` class Testament: ... ``` ### contents of tests/test\_testament.py ```` from domain import Testament def test_testament(): ... ``` In this scenario with the default options, pytest will collect the class [Testament]{.title-ref} from [tests/test_testament.py]{.title-ref} because it starts with [Test]{.title-ref}, even though in this case it is a production class being imported in the test module namespace. This behavior can now be prevented by setting the new `collect_imported_tests`{.interpreted-text role="confval"} configuration option to `false`, which will make pytest collect classes/functions from test files **only** if they are defined in that file. \-- by `FreerGit`{.interpreted-text role="user"} ```` - [#​12765](https://redirect.github.com/pytest-dev/pytest/issues/12765): Thresholds to trigger snippet truncation can now be set with `truncation_limit_lines`{.interpreted-text role="confval"} and `truncation_limit_chars`{.interpreted-text role="confval"}. See `truncation-params`{.interpreted-text role="ref"} for more information. - [#​13125](https://redirect.github.com/pytest-dev/pytest/issues/13125): `console_output_style`{.interpreted-text role="confval"} now supports `times` to show execution time of each test. - [#​13192](https://redirect.github.com/pytest-dev/pytest/issues/13192): `pytest.raises`{.interpreted-text role="func"} will now raise a warning when passing an empty string to `match`, as this will match against any value. Use `match="^$"` if you want to check that an exception has no message. - [#​13192](https://redirect.github.com/pytest-dev/pytest/issues/13192): `pytest.raises`{.interpreted-text role="func"} will now print a helpful string diff if matching fails and the match parameter has `^` and `$` and is otherwise escaped. - [#​13192](https://redirect.github.com/pytest-dev/pytest/issues/13192): You can now pass `with pytest.raises(check=fn): `{.interpreted-text role="func"}, where `fn` is a function which takes a raised exception and returns a boolean. The `raises` fails if no exception was raised (as usual), passes if an exception is raised and `fn` returns `True` (as well as `match` and the type matching, if specified, which are checked before), and propagates the exception if `fn` returns `False` (which likely also fails the test). - [#​13228](https://redirect.github.com/pytest-dev/pytest/issues/13228): `hidden-param`{.interpreted-text role="ref"} can now be used in `id` of `pytest.param`{.interpreted-text role="func"} or in `ids` of `Metafunc.parametrize `{.interpreted-text role="py:func"}. It hides the parameter set from the test name. - [#​13253](https://redirect.github.com/pytest-dev/pytest/issues/13253): New flag: `--disable-plugin-autoload `{.interpreted-text role="ref"} which works as an alternative to `PYTEST_DISABLE_PLUGIN_AUTOLOAD`{.interpreted-text role="envvar"} when setting environment variables is inconvenient; and allows setting it in config files with `addopts`{.interpreted-text role="confval"}. #### Improvements in existing functionality - [#​10224](https://redirect.github.com/pytest-dev/pytest/issues/10224): pytest's `short` and `long` traceback styles (`how-to-modifying-python-tb-printing`{.interpreted-text role="ref"}) now have partial `657`{.interpreted-text role="pep"} support and will show specific code segments in the traceback. ```pytest ================================= FAILURES ================================= _______________________ test_gets_correct_tracebacks _______________________ test_tracebacks.py:12: in test_gets_correct_tracebacks assert manhattan_distance(p1, p2) == 1 ^^^^^^^^^^^^^^^^^^^^^^^^^^ test_tracebacks.py:6: in manhattan_distance return abs(point_1.x - point_2.x) + abs(point_1.y - point_2.y) ^^^^^^^^^ E AttributeError: 'NoneType' object has no attribute 'x' ``` \-- by `ammaraskar`{.interpreted-text role="user"} - [#​11118](https://redirect.github.com/pytest-dev/pytest/issues/11118): Now `pythonpath`{.interpreted-text role="confval"} configures \[$PYTHONPATH]{.title-ref} earlier than before during the initialization process, which now also affects plugins loaded via the \[-p]{.title-ref} command-line option. \-- by `millerdev`{.interpreted-text role="user"} - [#​11381](https://redirect.github.com/pytest-dev/pytest/issues/11381): The `type` parameter of the `parser.addini` method now accepts \["int"]{.title-ref} and `"float"` parameters, facilitating the parsing of configuration values in the configuration file. Example: ```python def pytest_addoption(parser): parser.addini("int_value", type="int", default=2, help="my int value") parser.addini("float_value", type="float", default=4.2, help="my float value") ``` The \[pytest.ini]{.title-ref} file: ```ini [pytest] int_value = 3 float_value = 5.4 ``` - [#​11525](https://redirect.github.com/pytest-dev/pytest/issues/11525): Fixtures are now clearly represented in the output as a "fixture object", not as a normal function as before, making it easy for beginners to catch mistakes such as referencing a fixture declared in the same module but not requested in the test function. \-- by `the-compiler`{.interpreted-text role="user"} and `glyphack`{.interpreted-text role="user"} - [#​12426](https://redirect.github.com/pytest-dev/pytest/issues/12426): A warning is now issued when `pytest.mark.usefixtures ref`{.interpreted-text role="ref"} is used without specifying any fixtures. Previously, empty usefixtures markers were silently ignored. - [#​12707](https://redirect.github.com/pytest-dev/pytest/issues/12707): Exception chains can be navigated when dropped into Pdb in Python 3.13+. - [#​12736](https://redirect.github.com/pytest-dev/pytest/issues/12736): Added a new attribute \[name]{.title-ref} with the fixed value \["pytest tests"]{.title-ref} to the root tag \[testsuites]{.title-ref} of the junit-xml generated by pytest. This attribute is part of many junit-xml specifications and is even part of the \[junit-10.xsd]{.title-ref} specification that pytest's implementation is based on. - [#​12943](https://redirect.github.com/pytest-dev/pytest/issues/12943): If a test fails with an exceptiongroup with a single exception, the contained exception will now be displayed in the short test summary info. - [#​12958](https://redirect.github.com/pytest-dev/pytest/issues/12958): A number of `unraisable `{.interpreted-text role="ref"} enhancements: - Set the unraisable hook as early as possible and unset it as late as possible, to collect the most possible number of unraisable exceptions. - Call the garbage collector just before unsetting the unraisable hook, to collect any straggling exceptions. - Collect multiple unraisable exceptions per test phase. - Report the `tracemalloc`{.interpreted-text role="mod"} allocation traceback (if available). - Avoid using a generator based hook to allow handling `StopIteration`{.interpreted-text role="class"} in test failures. - Report the unraisable exception as the cause of the `pytest.PytestUnraisableExceptionWarning`{.interpreted-text role="class"} exception if raised. - Compute the `repr` of the unraisable object in the unraisable hook so you get the latest information if available, and should help with resurrection of the object. - [#​13010](https://redirect.github.com/pytest-dev/pytest/issues/13010): `pytest.approx`{.interpreted-text role="func"} now can compare collections that contain numbers and non-numbers mixed. - [#​13016](https://redirect.github.com/pytest-dev/pytest/issues/13016): A number of `threadexception `{.interpreted-text role="ref"} enhancements: - Set the excepthook as early as possible and unset it as late as possible, to collect the most possible number of unhandled exceptions from threads. - Collect multiple thread exceptions per test phase. - Report the `tracemalloc`{.interpreted-text role="mod"} allocation traceback (if available). - Avoid using a generator based hook to allow handling `StopIteration`{.interpreted-text role="class"} in test failures. - Report the thread exception as the cause of the `pytest.PytestUnhandledThreadExceptionWarning`{.interpreted-text role="class"} exception if raised. - Extract the `name` of the thread object in the excepthook which should help with resurrection of the thread. - [#​13031](https://redirect.github.com/pytest-dev/pytest/issues/13031): An empty parameter set as in `pytest.mark.parametrize([], ids=idfunc)` will no longer trigger a call to `idfunc` with internal objects. - [#​13115](https://redirect.github.com/pytest-dev/pytest/issues/13115): Allows supplying `ExceptionGroup[Exception]` and `BaseExceptionGroup[BaseException]` to `pytest.raises` to keep full typing on `ExceptionInfo `{.interpreted-text role="class"}: ```python with pytest.raises(ExceptionGroup[Exception]) as exc_info: some_function() ``` Parametrizing with other exception types remains an error - we do not check the types of child exceptions and thus do not permit code that might look like we do. - [#​13122](https://redirect.github.com/pytest-dev/pytest/issues/13122): The `--stepwise` mode received a number of improvements: - It no longer forgets the last failed test in case pytest is executed later without the flag. This enables the following workflow: 1. Execute pytest with `--stepwise`, pytest then stops at the first failing test; 2. Iteratively update the code and run the test in isolation, without the `--stepwise` flag (for example in an IDE), until it is fixed. 3. Execute pytest with `--stepwise` again and pytest will continue from the previously failed test, and if it passes, continue on to the next tests. Previously, at step 3, pytest would start from the beginning, forgetting the previously failed test. This change however might cause issues if the `--stepwise` mode is used far apart in time, as the state might get stale, so the internal state will be reset automatically in case the test suite changes (for now only the number of tests are considered for this, we might change/improve this on the future). - New `--stepwise-reset`/`--sw-reset` flag, allowing the user to explicitly reset the stepwise state and restart the workflow from the beginning. - [#​13308](https://redirect.github.com/pytest-dev/pytest/issues/13308): Added official support for Python 3.14. - [#​13380](https://redirect.github.com/pytest-dev/pytest/issues/13380): Fix `ExceptionGroup`{.interpreted-text role="class"} traceback filtering to exclude pytest internals. - [#​13415](https://redirect.github.com/pytest-dev/pytest/issues/13415): The author metadata of the BibTex example is now correctly formatted with last names following first names. An example of BibLaTex has been added. BibTex and BibLaTex examples now clearly indicate that what is cited is software. \-- by `willynilly`{.interpreted-text role="user"} - [#​13420](https://redirect.github.com/pytest-dev/pytest/issues/13420): Improved test collection performance by optimizing path resolution used in `FSCollector`. - [#​13457](https://redirect.github.com/pytest-dev/pytest/issues/13457): The error message about duplicate parametrization no longer displays an internal stack trace. - [#​4112](https://redirect.github.com/pytest-dev/pytest/issues/4112): Using `pytest.mark.usefixtures `{.interpreted-text role="ref"} on `pytest.param`{.interpreted-text role="func"} now produces an error instead of silently doing nothing. - [#​5473](https://redirect.github.com/pytest-dev/pytest/issues/5473): Replace \[:]{.title-ref} with \[;]{.title-ref} in the assertion rewrite warning message so it can be filtered using standard Python warning filters before calling `pytest.main`{.interpreted-text role="func"}. - ``` [#​6985](https://redirect.github.com/pytest-dev/pytest/issues/6985): Improved `pytest.approx`{.interpreted-text role="func"} to enhance the readability of value ranges and tolerances between 0.001 and 1000. ``` ```` : - The [repr]{.title-ref} method now provides clearer output for values within those ranges, making it easier to interpret the results. - Previously, the output for those ranges of values and tolerances was displayed in scientific notation (e.g., [42 ± 1.0e+00]{.title-ref}). The updated method now presents the tolerance as a decimal for better readability (e.g., [42 ± 1]{.title-ref}). Example: **Previous Output:** ``` console >>> pytest.approx(42, abs=1) 42 ± 1.0e+00 ``` **Current Output:** ``` console >>> pytest.approx(42, abs=1) 42 ± 1 ``` \-- by `fazeelghafoor`{.interpreted-text role="user"} ```` - [#​7683](https://redirect.github.com/pytest-dev/pytest/issues/7683): The formerly optional `pygments` dependency is now required, causing output always to be source-highlighted (unless disabled via the `--code-highlight=no` CLI option). #### Bug fixes - [#​10404](https://redirect.github.com/pytest-dev/pytest/issues/10404): Apply filterwarnings from config/cli as soon as possible, and revert them as late as possible so that warnings as errors are collected throughout the pytest run and before the unraisable and threadexcept hooks are removed. This allows very late warnings and unraisable/threadexcept exceptions to fail the test suite. This also changes the warning that the lsof plugin issues from PytestWarning to the new warning PytestFDWarning so it can be more easily filtered. - [#​11067](https://redirect.github.com/pytest-dev/pytest/issues/11067): The test report is now consistent regardless if the test xfailed via `pytest.mark.xfail `{.interpreted-text role="ref"} or `pytest.fail`{.interpreted-text role="func"}. Previously, *xfailed* tests via the marker would have the string `"reason: "` prefixed to the message, while those *xfailed* via the function did not. The prefix has been removed. - [#​12008](https://redirect.github.com/pytest-dev/pytest/issues/12008): In `11220`{.interpreted-text role="pr"}, an unintended change in reordering was introduced by changing the way indices were assigned to direct params. More specifically, before that change, the indices of direct params to metafunc's callspecs were assigned after all parametrizations took place. Now, that change is reverted. - [#​12863](https://redirect.github.com/pytest-dev/pytest/issues/12863): Fix applying markers, including `pytest.mark.parametrize `{.interpreted-text role="ref"} when placed above \[[@​staticmethod](https://redirect.github.com/staticmethod)]{.title-ref} or \[[@​classmethod](https://redirect.github.com/classmethod)]{.title-ref}. - [#​12929](https://redirect.github.com/pytest-dev/pytest/issues/12929): Handle StopIteration from test cases, setup and teardown correctly. - [#​12938](https://redirect.github.com/pytest-dev/pytest/issues/12938): Fixed `--durations-min` argument not respected if `-vv` is used. - [#​12946](https://redirect.github.com/pytest-dev/pytest/issues/12946): Fixed missing help for `pdb`{.interpreted-text role="mod"} commands wrapped by pytest -- by `adamchainz`{.interpreted-text role="user"}. - [#​12981](https://redirect.github.com/pytest-dev/pytest/issues/12981): Prevent exceptions in `pytest.Config.add_cleanup`{.interpreted-text role="func"} callbacks preventing further cleanups. - [#​13047](https://redirect.github.com/pytest-dev/pytest/issues/13047): Restore `pytest.approx`{.interpreted-text role="func"} handling of equality checks between \[bool]{.title-ref} and \[numpy.bool\_]{.title-ref} types. Comparing \[bool]{.title-ref} and \[numpy.bool\_]{.title-ref} using `pytest.approx`{.interpreted-text role="func"} accidentally changed in version \[8.3.4]{.title-ref} and \[8.3.5]{.title-ref} to no longer match: ```pycon >>> import numpy as np >>> from pytest import approx >>> [np.True_, np.True_] == pytest.approx([True, True]) False ``` This has now been fixed: ```pycon >>> [np.True_, np.True_] == pytest.approx([True, True]) True ``` - [#​13119](https://redirect.github.com/pytest-dev/pytest/issues/13119): Improved handling of invalid regex patterns for filter warnings by providing a clear error message. - [#​13175](https://redirect.github.com/pytest-dev/pytest/issues/13175): The diff is now also highlighted correctly when comparing two strings. - [#​13248](https://redirect.github.com/pytest-dev/pytest/issues/13248): Fixed an issue where passing a `scope` in `Metafunc.parametrize `{.interpreted-text role="py:func"} with `indirect=True` could result in other fixtures being unable to depend on the parametrized fixture. - [#​13291](https://redirect.github.com/pytest-dev/pytest/issues/13291): Fixed `repr` of `attrs` objects in assertion failure messages when using `attrs>=25.2`. - [#​13312](https://redirect.github.com/pytest-dev/pytest/issues/13312): Fixed a possible `KeyError` crash on PyPy during collection of tests involving higher-scoped parameters. - [#​13345](https://redirect.github.com/pytest-dev/pytest/issues/13345): Fix type hints for `pytest.TestReport.when`{.interpreted-text role="attr"} and `pytest.TestReport.location`{.interpreted-text role="attr"}. - [#​13377](https://redirect.github.com/pytest-dev/pytest/issues/13377): Fixed handling of test methods with positional-only parameter syntax. Now, methods are supported that formally define `self` as positional-only and/or fixture parameters as keyword-only, e.g.: ```python class TestClass: def test_method(self, /, *, fixture): ... ``` Before, this caused an internal error in pytest. - [#​13384](https://redirect.github.com/pytest-dev/pytest/issues/13384): Fixed an issue where pytest could report negative durations. - [#​13420](https://redirect.github.com/pytest-dev/pytest/issues/13420): Added `lru_cache` to `nodes._check_initialpaths_for_relpath`. - [#​9037](https://redirect.github.com/pytest-dev/pytest/issues/9037): Honor `disable_test_id_escaping_and_forfeit_all_rights_to_community_support`{.interpreted-text role="confval"} when escaping ids in parametrized tests. #### Improved documentation - [#​12535](https://redirect.github.com/pytest-dev/pytest/issues/12535): \[This example]{.title-ref}<> showed `print` statements that do not exactly reflect what the different branches actually do. The fix makes the example more precise. - [#​13218](https://redirect.github.com/pytest-dev/pytest/issues/13218): Pointed out in the `pytest.approx`{.interpreted-text role="func"} documentation that it considers booleans unequal to numeric zero or one. - [#​13221](https://redirect.github.com/pytest-dev/pytest/issues/13221): Improved grouping of CLI options in the `--help` output. - [#​6649](https://redirect.github.com/pytest-dev/pytest/issues/6649): Added `~pytest.TerminalReporter`{.interpreted-text role="class"} to the `api-reference`{.interpreted-text role="ref"} documentation page. - [#​8612](https://redirect.github.com/pytest-dev/pytest/issues/8612): Add a recipe for handling abstract test classes in the documentation. A new example has been added to the documentation to demonstrate how to use a mixin class to handle abstract test classes without manually setting the `__test__` attribute for subclasses. This ensures that subclasses of abstract test classes are automatically collected by pytest. #### Packaging updates and notes for downstreams - [#​13317](https://redirect.github.com/pytest-dev/pytest/issues/13317): Specified minimum allowed versions of `colorama`, `iniconfig`, and `packaging`; and bumped the minimum allowed version of `exceptiongroup` for `python_version<'3.11'` from a release candidate to a full release. #### Contributor-facing changes - [#​12017](https://redirect.github.com/pytest-dev/pytest/issues/12017): Mixed internal improvements: - Migrate formatting to f-strings in some tests. - Use type-safe constructs in JUnitXML tests. - Moved`MockTiming` into `_pytest.timing`. \-- by `RonnyPfannschmidt`{.interpreted-text role="user"} - [#​12647](https://redirect.github.com/pytest-dev/pytest/issues/12647): Fixed running the test suite with the `hypothesis` pytest plugin. #### Miscellaneous internal changes - [#​6649](https://redirect.github.com/pytest-dev/pytest/issues/6649): Added `~pytest.TerminalReporter`{.interpreted-text role="class"} to the public pytest API, as it is part of the signature of the `pytest_terminal_summary`{.interpreted-text role="hook"} hook. ### [`v8.3.5`](https://redirect.github.com/pytest-dev/pytest/releases/tag/8.3.5) [Compare Source](https://redirect.github.com/pytest-dev/pytest/compare/8.3.4...8.3.5) ### pytest 8.3.5 (2025-03-02) #### Bug fixes - [#​11777](https://redirect.github.com/pytest-dev/pytest/issues/11777): Fixed issue where sequences were still being shortened even with `-vv` verbosity. - [#​12888](https://redirect.github.com/pytest-dev/pytest/issues/12888): Fixed broken input when using Python 3.13+ and a `libedit` build of Python, such as on macOS or with uv-managed Python binaries from the `python-build-standalone` project. This could manifest e.g. by a broken prompt when using `Pdb`, or seeing empty inputs with manual usage of `input()` and suspended capturing. - [#​13026](https://redirect.github.com/pytest-dev/pytest/issues/13026): Fixed `AttributeError`{.interpreted-text role="class"} crash when using `--import-mode=importlib` when top-level directory same name as another module of the standard library. - [#​13053](https://redirect.github.com/pytest-dev/pytest/issues/13053): Fixed a regression in pytest 8.3.4 where, when using `--import-mode=importlib`, a directory containing py file with the same name would cause an `ImportError` - [#​13083](https://redirect.github.com/pytest-dev/pytest/issues/13083): Fixed issue where pytest could crash if one of the collected directories got removed during collection. #### Improved documentation - [#​12842](https://redirect.github.com/pytest-dev/pytest/issues/12842): Added dedicated page about using types with pytest. See `types`{.interpreted-text role="ref"} for detailed usage. #### Contributor-facing changes - [#​13112](https://redirect.github.com/pytest-dev/pytest/issues/13112): Fixed selftest failures in `test_terminal.py` with Pygments >= 2.19.0 - [#​13256](https://redirect.github.com/pytest-dev/pytest/issues/13256): Support for Towncrier versions released in 2024 has been re-enabled when building Sphinx docs -- by `webknjaz`{.interpreted-text role="user"}. ### [`v8.3.4`](https://redirect.github.com/pytest-dev/pytest/releases/tag/8.3.4) [Compare Source](https://redirect.github.com/pytest-dev/pytest/compare/8.3.3...8.3.4) ### pytest 8.3.4 (2024-12-01) #### Bug fixes - [#​12592](https://redirect.github.com/pytest-dev/pytest/issues/12592): Fixed `KeyError`{.interpreted-text role="class"} crash when using `--import-mode=importlib` in a directory l
--- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://redirect.github.com/renovatebot/renovate/discussions) if that's undesired. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/googleapis/python-datastore). --------- Co-authored-by: Anthonios Partheniou --- datastore/samples/snippets/requirements-test.txt | 4 +++- datastore/samples/snippets/requirements.txt | 2 +- .../samples/snippets/schedule-export/requirements-test.txt | 4 +++- datastore/samples/snippets/schedule-export/requirements.txt | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/datastore/samples/snippets/requirements-test.txt b/datastore/samples/snippets/requirements-test.txt index 81c08fe2c07..6e6ff3ab179 100644 --- a/datastore/samples/snippets/requirements-test.txt +++ b/datastore/samples/snippets/requirements-test.txt @@ -1,5 +1,7 @@ backoff===1.11.1; python_version < "3.7" backoff==2.2.1; python_version >= "3.7" pytest===7.4.3; python_version == '3.7' -pytest==8.3.2; python_version >= '3.8' +pytest===8.3.5; python_version == '3.8' +pytest===8.4.2; python_version == '3.9' +pytest==8.4.2; python_version >= '3.10' flaky==3.8.1 diff --git a/datastore/samples/snippets/requirements.txt b/datastore/samples/snippets/requirements.txt index 8816d485f0f..7852f23b24e 100644 --- a/datastore/samples/snippets/requirements.txt +++ b/datastore/samples/snippets/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.20.0 \ No newline at end of file +google-cloud-datastore==2.23.0 \ No newline at end of file diff --git a/datastore/samples/snippets/schedule-export/requirements-test.txt b/datastore/samples/snippets/schedule-export/requirements-test.txt index 6c461db6f4c..a664595f136 100644 --- a/datastore/samples/snippets/schedule-export/requirements-test.txt +++ b/datastore/samples/snippets/schedule-export/requirements-test.txt @@ -1,2 +1,4 @@ pytest===7.4.3; python_version == '3.7' -pytest==8.3.2; python_version >= '3.8' \ No newline at end of file +pytest===8.3.5; python_version == '3.8' +pytest===8.4.2; python_version == '3.9' +pytest==9.0.2; python_version >= '3.10' diff --git a/datastore/samples/snippets/schedule-export/requirements.txt b/datastore/samples/snippets/schedule-export/requirements.txt index 7f50395730c..fa16c1e95ab 100644 --- a/datastore/samples/snippets/schedule-export/requirements.txt +++ b/datastore/samples/snippets/schedule-export/requirements.txt @@ -1 +1 @@ -google-cloud-datastore==2.20.0 +google-cloud-datastore==2.23.0 From a0ce1b4b94f6053f7d24f2bad3b5d820f086516f Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Fri, 13 Feb 2026 19:12:04 +0000 Subject: [PATCH 50/52] chore(deps): update all dependencies (#670) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | Type | Update | |---|---|---|---|---|---| | [pytest](https://redirect.github.com/pytest-dev/pytest) ([changelog](https://docs.pytest.org/en/stable/changelog.html)) | `==8.4.2` → `==9.0.2` | ![age](https://developer.mend.io/api/mc/badges/age/pypi/pytest/9.0.2?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/pytest/8.4.2/9.0.2?slim=true) | | major | | [python](https://redirect.github.com/actions/python-versions) | `3.10` → `3.14` | ![age](https://developer.mend.io/api/mc/badges/age/github-releases/actions%2fpython-versions/3.14.3?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/github-releases/actions%2fpython-versions/3.10.19/3.14.3?slim=true) | uses-with | minor | --- ### Release Notes
pytest-dev/pytest (pytest) ### [`v9.0.2`](https://redirect.github.com/pytest-dev/pytest/releases/tag/9.0.2) [Compare Source](https://redirect.github.com/pytest-dev/pytest/compare/9.0.1...9.0.2) ### pytest 9.0.2 (2025-12-06) #### Bug fixes - [#​13896](https://redirect.github.com/pytest-dev/pytest/issues/13896): The terminal progress feature added in pytest 9.0.0 has been disabled by default, except on Windows, due to compatibility issues with some terminal emulators. You may enable it again by passing `-p terminalprogress`. We may enable it by default again once compatibility improves in the future. Additionally, when the environment variable `TERM` is `dumb`, the escape codes are no longer emitted, even if the plugin is enabled. - [#​13904](https://redirect.github.com/pytest-dev/pytest/issues/13904): Fixed the TOML type of the `tmp_path_retention_count` settings in the API reference from number to string. - [#​13946](https://redirect.github.com/pytest-dev/pytest/issues/13946): The private `config.inicfg` attribute was changed in a breaking manner in pytest 9.0.0. Due to its usage in the ecosystem, it is now restored to working order using a compatibility shim. It will be deprecated in pytest 9.1 and removed in pytest 10. - [#​13965](https://redirect.github.com/pytest-dev/pytest/issues/13965): Fixed quadratic-time behavior when handling `unittest` subtests in Python 3.10. #### Improved documentation - [#​4492](https://redirect.github.com/pytest-dev/pytest/issues/4492): The API Reference now contains cross-reference-able documentation of `pytest's command-line flags `. ### [`v9.0.1`](https://redirect.github.com/pytest-dev/pytest/releases/tag/9.0.1) [Compare Source](https://redirect.github.com/pytest-dev/pytest/compare/9.0.0...9.0.1) ### pytest 9.0.1 (2025-11-12) #### Bug fixes - [#​13895](https://redirect.github.com/pytest-dev/pytest/issues/13895): Restore support for skipping tests via `raise unittest.SkipTest`. - [#​13896](https://redirect.github.com/pytest-dev/pytest/issues/13896): The terminal progress plugin added in pytest 9.0 is now automatically disabled when iTerm2 is detected, it generated desktop notifications instead of the desired functionality. - [#​13904](https://redirect.github.com/pytest-dev/pytest/issues/13904): Fixed the TOML type of the verbosity settings in the API reference from number to string. - [#​13910](https://redirect.github.com/pytest-dev/pytest/issues/13910): Fixed UserWarning: Do not expect file\_or\_dir on some earlier Python 3.12 and 3.13 point versions. #### Packaging updates and notes for downstreams - [#​13933](https://redirect.github.com/pytest-dev/pytest/issues/13933): The tox configuration has been adjusted to make sure the desired version string can be passed into its `package_env` through the `SETUPTOOLS_SCM_PRETEND_VERSION_FOR_PYTEST` environment variable as a part of the release process -- by `webknjaz`. #### Contributor-facing changes - [#​13891](https://redirect.github.com/pytest-dev/pytest/issues/13891), [#​13942](https://redirect.github.com/pytest-dev/pytest/issues/13942): The CI/CD part of the release automation is now capable of creating GitHub Releases without having a Git checkout on disk -- by `bluetech` and `webknjaz`. - [#​13933](https://redirect.github.com/pytest-dev/pytest/issues/13933): The tox configuration has been adjusted to make sure the desired version string can be passed into its `package_env` through the `SETUPTOOLS_SCM_PRETEND_VERSION_FOR_PYTEST` environment variable as a part of the release process -- by `webknjaz`. ### [`v9.0.0`](https://redirect.github.com/pytest-dev/pytest/releases/tag/9.0.0) [Compare Source](https://redirect.github.com/pytest-dev/pytest/compare/8.4.2...9.0.0) ### pytest 9.0.0 (2025-11-05) #### New features - [#​1367](https://redirect.github.com/pytest-dev/pytest/issues/1367): **Support for subtests** has been added. `subtests ` are an alternative to parametrization, useful in situations where the parametrization values are not all known at collection time. Example: ```python def contains_docstring(p: Path) -> bool: """Return True if the given Python file contains a top-level docstring.""" ... def test_py_files_contain_docstring(subtests: pytest.Subtests) -> None: for path in Path.cwd().glob("*.py"): with subtests.test(path=str(path)): assert contains_docstring(path) ``` Each assert failure or error is caught by the context manager and reported individually, giving a clear picture of all files that are missing a docstring. In addition, `unittest.TestCase.subTest` is now also supported. This feature was originally implemented as a separate plugin in [pytest-subtests](https://redirect.github.com/pytest-dev/pytest-subtests), but since then has been merged into the core. > \[!NOTE] > This feature is experimental and will likely evolve in future releases. By that we mean that we might change how subtests are reported on failure, but the functionality and how to use it are stable. - [#​13743](https://redirect.github.com/pytest-dev/pytest/issues/13743): Added support for **native TOML configuration files**. While pytest, since version 6, supports configuration in `pyproject.toml` files under `[tool.pytest.ini_options]`, it does so in an "INI compatibility mode", where all configuration values are treated as strings or list of strings. Now, pytest supports the native TOML data model. In `pyproject.toml`, the native TOML configuration is under the `[tool.pytest]` table. ```toml # pyproject.toml [tool.pytest] minversion = "9.0" addopts = ["-ra", "-q"] testpaths = [ "tests", "integration", ] ``` The `[tool.pytest.ini_options]` table remains supported, but both tables cannot be used at the same time. If you prefer to use a separate configuration file, or don't use `pyproject.toml`, you can use `pytest.toml` or `.pytest.toml`: ```toml # pytest.toml or .pytest.toml [pytest] minversion = "9.0" addopts = ["-ra", "-q"] testpaths = [ "tests", "integration", ] ``` The documentation now (sometimes) shows configuration snippets in both TOML and INI formats, in a tabbed interface. See `config file formats` for full details. - [#​13823](https://redirect.github.com/pytest-dev/pytest/issues/13823): Added a **"strict mode"** enabled by the `strict` configuration option. When set to `true`, the `strict` option currently enables - `strict_config` - `strict_markers` - `strict_parametrization_ids` - `strict_xfail` The individual strictness options can be explicitly set to override the global `strict` setting. The previously-deprecated `--strict` command-line flag now enables strict mode. If pytest adds new strictness options in the future, they will also be enabled in strict mode. Therefore, you should only enable strict mode if you use a pinned/locked version of pytest, or if you want to proactively adopt new strictness options as they are added. See `strict mode` for more details. - [#​13737](https://redirect.github.com/pytest-dev/pytest/issues/13737): Added the `strict_parametrization_ids` configuration option. When set, pytest emits an error if it detects non-unique parameter set IDs, rather than automatically making the IDs unique by adding 0, 1, ... to them. This can be particularly useful for catching unintended duplicates. - [#​13072](https://redirect.github.com/pytest-dev/pytest/issues/13072): Added support for displaying test session **progress in the terminal tab** using the [OSC 9;4;](https://conemu.github.io/en/AnsiEscapeCodes.html#ConEmu_specific_OSC) ANSI sequence. When pytest runs in a supported terminal emulator like ConEmu, Gnome Terminal, Ptyxis, Windows Terminal, Kitty or Ghostty, you'll see the progress in the terminal tab or window, allowing you to monitor pytest's progress at a glance. This feature is automatically enabled when running in a TTY. It is implemented as an internal plugin. If needed, it can be disabled as follows: - On a user level, using `-p no:terminalprogress` on the command line or via an environment variable `PYTEST_ADDOPTS='-p no:terminalprogress'`. - On a project configuration level, using `addopts = "-p no:terminalprogress"`. - [#​478](https://redirect.github.com/pytest-dev/pytest/issues/478): Support PEP420 (implicit namespace packages) as --pyargs target when `consider_namespace_packages` is true in the config. Previously, this option only impacted package imports, now it also impacts tests discovery. - [#​13678](https://redirect.github.com/pytest-dev/pytest/issues/13678): Added a new `faulthandler_exit_on_timeout` configuration option set to "false" by default to let faulthandler interrupt the pytest process after a timeout in case of deadlock. Previously, a faulthandler timeout would only dump the traceback of all threads to stderr, but would not interrupt the pytest process. \-- by `ogrisel`. - [#​13829](https://redirect.github.com/pytest-dev/pytest/issues/13829): Added support for configuration option aliases via the `aliases` parameter in `Parser.addini() `. Plugins can now register alternative names for configuration options, allowing for more flexibility in configuration naming and supporting backward compatibility when renaming options. The canonical name always takes precedence if both the canonical name and an alias are specified in the configuration file. #### Improvements in existing functionality - [#​13330](https://redirect.github.com/pytest-dev/pytest/issues/13330): Having pytest configuration spread over more than one file (for example having both a `pytest.ini` file and `pyproject.toml` with a `[tool.pytest.ini_options]` table) will now print a warning to make it clearer to the user that only one of them is actually used. \-- by `sgaist` - [#​13574](https://redirect.github.com/pytest-dev/pytest/issues/13574): The single argument `--version` no longer loads the entire plugin infrastructure, making it faster and more reliable when displaying only the pytest version. Passing `--version` twice (e.g., `pytest --version --version`) retains the original behavior, showing both the pytest version and plugin information. > \[!NOTE] > Since `--version` is now processed early, it only takes effect when passed directly via the command line. It will not work if set through other mechanisms, such as `PYTEST_ADDOPTS` or `addopts`. - [#​13823](https://redirect.github.com/pytest-dev/pytest/issues/13823): Added `strict_xfail` as an alias to the `xfail_strict` option, `strict_config` as an alias to the `--strict-config` flag, and `strict_markers` as an alias to the `--strict-markers` flag. This makes all strictness options consistently have configuration options with the prefix `strict_`. - [#​13700](https://redirect.github.com/pytest-dev/pytest/issues/13700): --junitxml no longer prints the generated xml file summary at the end of the pytest session when --quiet is given. - [#​13732](https://redirect.github.com/pytest-dev/pytest/issues/13732): Previously, when filtering warnings, pytest would fail if the filter referenced a class that could not be imported. Now, this only outputs a message indicating the problem. - [#​13859](https://redirect.github.com/pytest-dev/pytest/issues/13859): Clarify the error message for pytest.raises() when a regex match fails. - [#​13861](https://redirect.github.com/pytest-dev/pytest/issues/13861): Better sentence structure in a test's expected error message. Previously, the error message would be "expected exception must be \, but got \". Now, it is "Expected \, but got \". #### Removals and backward incompatible breaking changes - [#​12083](https://redirect.github.com/pytest-dev/pytest/issues/12083): Fixed a bug where an invocation such as pytest a/ a/b would cause only tests from a/b to run, and not other tests under a/. The fix entails a few breaking changes to how such overlapping arguments and duplicates are handled: 1. pytest a/b a/ or pytest a/ a/b are equivalent to pytest a; if an argument overlaps another arguments, only the prefix remains. 2. pytest x.py x.py is equivalent to pytest x.py; previously such an invocation was taken as an explicit request to run the tests from the file twice. If you rely on these behaviors, consider using `--keep-duplicates `, which retains its existing behavior (including the bug). - [#​13719](https://redirect.github.com/pytest-dev/pytest/issues/13719): Support for Python 3.9 is dropped following its end of life. - [#​13766](https://redirect.github.com/pytest-dev/pytest/issues/13766): Previously, pytest would assume it was running in a CI/CD environment if either of the environment variables $CI or $BUILD\_NUMBER was defined; now, CI mode is only activated if at least one of those variables is defined and set to a *non-empty* value. - [#​13779](https://redirect.github.com/pytest-dev/pytest/issues/13779): **PytestRemovedIn9Warning deprecation warnings are now errors by default.** Following our plan to remove deprecated features with as little disruption as possible, all warnings of type `PytestRemovedIn9Warning` now generate errors instead of warning messages by default. **The affected features will be effectively removed in pytest 9.1**, so please consult the `deprecations` section in the docs for directions on how to update existing code. In the pytest `9.0.X` series, it is possible to change the errors back into warnings as a stopgap measure by adding this to your `pytest.ini` file: ```ini [pytest] filterwarnings = ignore::pytest.PytestRemovedIn9Warning ``` But this will stop working when pytest `9.1` is released. **If you have concerns** about the removal of a specific feature, please add a comment to `13779`. #### Deprecations (removal in next major release) - [#​13807](https://redirect.github.com/pytest-dev/pytest/issues/13807): `monkeypatch.syspath_prepend() ` now issues a deprecation warning when the prepended path contains legacy namespace packages (those using `pkg_resources.declare_namespace()`). Users should migrate to native namespace packages (`420`). See `monkeypatch-fixup-namespace-packages` for details. #### Bug fixes - [#​13445](https://redirect.github.com/pytest-dev/pytest/issues/13445): Made the type annotations of `pytest.skip` and friends more spec-complaint to have them work across more type checkers. - [#​13537](https://redirect.github.com/pytest-dev/pytest/issues/13537): Fixed a bug in which `ExceptionGroup` with only `Skipped` exceptions in teardown was not handled correctly and showed as error. - [#​13598](https://redirect.github.com/pytest-dev/pytest/issues/13598): Fixed possible collection confusion on Windows when short paths and symlinks are involved. - [#​13716](https://redirect.github.com/pytest-dev/pytest/issues/13716): Fixed a bug where a nonsensical invocation like `pytest x.py[a]` (a file cannot be parametrized) was silently treated as `pytest x.py`. This is now a usage error. - [#​13722](https://redirect.github.com/pytest-dev/pytest/issues/13722): Fixed a misleading assertion failure message when using `pytest.approx` on mappings with differing lengths. - [#​13773](https://redirect.github.com/pytest-dev/pytest/issues/13773): Fixed the static fixture closure calculation to properly consider transitive dependencies requested by overridden fixtures. - [#​13816](https://redirect.github.com/pytest-dev/pytest/issues/13816): Fixed `pytest.approx` which now returns a clearer error message when comparing mappings with different keys. - [#​13849](https://redirect.github.com/pytest-dev/pytest/issues/13849): Hidden `.pytest.ini` files are now picked up as the config file even if empty. This was an inconsistency with non-hidden `pytest.ini`. - [#​13865](https://redirect.github.com/pytest-dev/pytest/issues/13865): Fixed --show-capture with --tb=line. - [#​13522](https://redirect.github.com/pytest-dev/pytest/issues/13522): Fixed `pytester` in subprocess mode ignored all :attr\`pytester.plugins \\` except the first. Fixed `pytester` in subprocess mode silently ignored non-str `pytester.plugins `. Now it errors instead. If you are affected by this, specify the plugin by name, or switch the affected tests to use `pytester.runpytest_inprocess ` explicitly instead. #### Packaging updates and notes for downstreams - [#​13791](https://redirect.github.com/pytest-dev/pytest/issues/13791): Minimum requirements on `iniconfig` and `packaging` were bumped to `1.0.1` and `22.0.0`, respectively. #### Contributor-facing changes - [#​12244](https://redirect.github.com/pytest-dev/pytest/issues/12244): Fixed self-test failures when TERM=dumb. - [#​12474](https://redirect.github.com/pytest-dev/pytest/issues/12474): Added scheduled GitHub Action Workflow to run Sphinx linkchecks in repo documentation. - [#​13621](https://redirect.github.com/pytest-dev/pytest/issues/13621): pytest's own testsuite now handles the `lsof` command hanging (e.g. due to unreachable network filesystems), with the affected selftests being skipped after 10 seconds. - [#​13638](https://redirect.github.com/pytest-dev/pytest/issues/13638): Fixed deprecated `gh pr new` command in `scripts/prepare-release-pr.py`. The script now uses `gh pr create` which is compatible with GitHub CLI v2.0+. - [#​13695](https://redirect.github.com/pytest-dev/pytest/issues/13695): Flush stdout and stderr in Pytester.run to avoid truncated outputs in test\_faulthandler.py::test\_timeout on CI -- by `ogrisel`. - [#​13771](https://redirect.github.com/pytest-dev/pytest/issues/13771): Skip test\_do\_not\_collect\_symlink\_siblings on Windows environments without symlink support to avoid false negatives. - [#​13841](https://redirect.github.com/pytest-dev/pytest/issues/13841): `tox>=4` is now required when contributing to pytest. - [#​13625](https://redirect.github.com/pytest-dev/pytest/issues/13625): Added missing docstrings to `pytest_addoption()`, `pytest_configure()`, and `cacheshow()` functions in `cacheprovider.py`. #### Miscellaneous internal changes - [#​13830](https://redirect.github.com/pytest-dev/pytest/issues/13830): Configuration overrides (`-o`/`--override-ini`) are now processed during startup rather than during `config.getini() `.
actions/python-versions (python) ### [`v3.14.3`](https://redirect.github.com/actions/python-versions/releases/tag/3.14.3-21673711214): 3.14.3 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.14.2-20014991423...3.14.3-21673711214) Python 3.14.3 ### [`v3.14.2`](https://redirect.github.com/actions/python-versions/releases/tag/3.14.2-20014991423): 3.14.2 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.14.1-19879739908...3.14.2-20014991423) Python 3.14.2 ### [`v3.14.1`](https://redirect.github.com/actions/python-versions/releases/tag/3.14.1-19879739908): 3.14.1 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.14.0-18313368925...3.14.1-19879739908) Python 3.14.1 ### [`v3.14.0`](https://redirect.github.com/actions/python-versions/releases/tag/3.14.0-18313368925): 3.14.0 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.13.12-21673645133...3.14.0-18313368925) Python 3.14.0 ### [`v3.13.12`](https://redirect.github.com/actions/python-versions/releases/tag/3.13.12-21673645133): 3.13.12 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.13.11-20014977833...3.13.12-21673645133) Python 3.13.12 ### [`v3.13.11`](https://redirect.github.com/actions/python-versions/releases/tag/3.13.11-20014977833): 3.13.11 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.13.10-19879712315...3.13.11-20014977833) Python 3.13.11 ### [`v3.13.10`](https://redirect.github.com/actions/python-versions/releases/tag/3.13.10-19879712315): 3.13.10 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.13.9-18515951191...3.13.10-19879712315) Python 3.13.10 ### [`v3.13.9`](https://redirect.github.com/actions/python-versions/releases/tag/3.13.9-18515951191): 3.13.9 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.13.8-18331000654...3.13.9-18515951191) Python 3.13.9 ### [`v3.13.8`](https://redirect.github.com/actions/python-versions/releases/tag/3.13.8-18331000654): 3.13.8 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.13.7-16980743123...3.13.8-18331000654) Python 3.13.8 ### [`v3.13.7`](https://redirect.github.com/actions/python-versions/releases/tag/3.13.7-16980743123): 3.13.7 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.13.6-16792117939...3.13.7-16980743123) Python 3.13.7 ### [`v3.13.6`](https://redirect.github.com/actions/python-versions/releases/tag/3.13.6-16792117939): 3.13.6 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.13.5-15601068749...3.13.6-16792117939) Python 3.13.6 ### [`v3.13.5`](https://redirect.github.com/actions/python-versions/releases/tag/3.13.5-15601068749): 3.13.5 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.13.4-15433317575...3.13.5-15601068749) Python 3.13.5 ### [`v3.13.4`](https://redirect.github.com/actions/python-versions/releases/tag/3.13.4-15433317575): 3.13.4 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.13.3-14344076652...3.13.4-15433317575) Python 3.13.4 ### [`v3.13.3`](https://redirect.github.com/actions/python-versions/releases/tag/3.13.3-14344076652): 3.13.3 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.13.2-13708744326...3.13.3-14344076652) Python 3.13.3 ### [`v3.13.2`](https://redirect.github.com/actions/python-versions/releases/tag/3.13.2-13708744326): 3.13.2 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.13.1-13437882550...3.13.2-13708744326) Python 3.13.2 ### [`v3.13.1`](https://redirect.github.com/actions/python-versions/releases/tag/3.13.1-13437882550): 3.13.1 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.13.0-13707372259...3.13.1-13437882550) Python 3.13.1 ### [`v3.13.0`](https://redirect.github.com/actions/python-versions/releases/tag/3.13.0-13707372259): 3.13.0 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.12.12-18393146713...3.13.0-13707372259) Python 3.13.0 ### [`v3.12.12`](https://redirect.github.com/actions/python-versions/releases/tag/3.12.12-18393146713): 3.12.12 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.12.11-15433310049...3.12.12-18393146713) Python 3.12.12 ### [`v3.12.11`](https://redirect.github.com/actions/python-versions/releases/tag/3.12.11-15433310049): 3.12.11 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.12.10-14343898437...3.12.11-15433310049) Python 3.12.11 ### [`v3.12.10`](https://redirect.github.com/actions/python-versions/releases/tag/3.12.10-14343898437): 3.12.10 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.12.9-13149478207...3.12.10-14343898437) Python 3.12.10 ### [`v3.12.9`](https://redirect.github.com/actions/python-versions/releases/tag/3.12.9-13149478207): 3.12.9 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.12.8-12154062663...3.12.9-13149478207) Python 3.12.9 ### [`v3.12.8`](https://redirect.github.com/actions/python-versions/releases/tag/3.12.8-12154062663): 3.12.8 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.12.7-11128208086...3.12.8-12154062663) Python 3.12.8 ### [`v3.12.7`](https://redirect.github.com/actions/python-versions/releases/tag/3.12.7-11128208086): 3.12.7 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.12.6-10765725458...3.12.7-11128208086) Python 3.12.7 ### [`v3.12.6`](https://redirect.github.com/actions/python-versions/releases/tag/3.12.6-10765725458): 3.12.6 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.12.5-10375840348...3.12.6-10765725458) Python 3.12.6 ### [`v3.12.5`](https://redirect.github.com/actions/python-versions/releases/tag/3.12.5-10375840348): 3.12.5 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.12.4-9947065640...3.12.5-10375840348) Python 3.12.5 ### [`v3.12.4`](https://redirect.github.com/actions/python-versions/releases/tag/3.12.4-9947065640): 3.12.4 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.12.3-11057844995...3.12.4-9947065640) Python 3.12.4 ### [`v3.12.3`](https://redirect.github.com/actions/python-versions/releases/tag/3.12.3-11057844995): 3.12.3 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.12.2-11057786931...3.12.3-11057844995) Python 3.12.3 ### [`v3.12.2`](https://redirect.github.com/actions/python-versions/releases/tag/3.12.2-11057786931): 3.12.2 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.12.1-11057762749...3.12.2-11057786931) Python 3.12.2 ### [`v3.12.1`](https://redirect.github.com/actions/python-versions/releases/tag/3.12.1-11057762749): 3.12.1 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.12.0-11057302691...3.12.1-11057762749) Python 3.12.1 ### [`v3.12.0`](https://redirect.github.com/actions/python-versions/releases/tag/3.12.0-11057302691): 3.12.0 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.11.14-18393181605...3.12.0-11057302691) Python 3.12.0 ### [`v3.11.14`](https://redirect.github.com/actions/python-versions/releases/tag/3.11.14-18393181605): 3.11.14 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.11.13-15433298024...3.11.14-18393181605) Python 3.11.14 ### [`v3.11.13`](https://redirect.github.com/actions/python-versions/releases/tag/3.11.13-15433298024): 3.11.13 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.11.12-14343939122...3.11.13-15433298024) Python 3.11.13 ### [`v3.11.12`](https://redirect.github.com/actions/python-versions/releases/tag/3.11.12-14343939122): 3.11.12 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.11.11-12160100664...3.11.12-14343939122) Python 3.11.12 ### [`v3.11.11`](https://redirect.github.com/actions/python-versions/releases/tag/3.11.11-12160100664): 3.11.11 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.11.10-10765870205...3.11.11-12160100664) Python 3.11.11 ### [`v3.11.10`](https://redirect.github.com/actions/python-versions/releases/tag/3.11.10-10765870205): 3.11.10 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.11.9-9947079978...3.11.10-10765870205) Python 3.11.10 ### [`v3.11.9`](https://redirect.github.com/actions/python-versions/releases/tag/3.11.9-9947079978): 3.11.9 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.11.8-11113201752...3.11.9-9947079978) Python 3.11.9 ### [`v3.11.8`](https://redirect.github.com/actions/python-versions/releases/tag/3.11.8-11113201752): 3.11.8 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.11.7-11113197120...3.11.8-11113201752) Python 3.11.8 ### [`v3.11.7`](https://redirect.github.com/actions/python-versions/releases/tag/3.11.7-11113197120): 3.11.7 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.11.6-11113179737...3.11.7-11113197120) Python 3.11.7 ### [`v3.11.6`](https://redirect.github.com/actions/python-versions/releases/tag/3.11.6-11113179737): 3.11.6 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.11.5-11113174019...3.11.6-11113179737) Python 3.11.6 ### [`v3.11.5`](https://redirect.github.com/actions/python-versions/releases/tag/3.11.5-11113174019): 3.11.5 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.11.4-11113170699...3.11.5-11113174019) Python 3.11.5 ### [`v3.11.4`](https://redirect.github.com/actions/python-versions/releases/tag/3.11.4-11113170699): 3.11.4 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.11.3-11059198104...3.11.4-11113170699) Python 3.11.4 ### [`v3.11.3`](https://redirect.github.com/actions/python-versions/releases/tag/3.11.3-11059198104): 3.11.3 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.11.2-11059137522...3.11.3-11059198104) Python 3.11.3 ### [`v3.11.2`](https://redirect.github.com/actions/python-versions/releases/tag/3.11.2-11059137522): 3.11.2 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.11.1-11058799881...3.11.2-11059137522) Python 3.11.2 ### [`v3.11.1`](https://redirect.github.com/actions/python-versions/releases/tag/3.11.1-11058799881): 3.11.1 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.11.0-11058707212...3.11.1-11058799881) Python 3.11.1 ### [`v3.11.0`](https://redirect.github.com/actions/python-versions/releases/tag/3.11.0-11058707212): 3.11.0 [Compare Source](https://redirect.github.com/actions/python-versions/compare/3.10.19-18393196481...3.11.0-11058707212) Python 3.11.0
--- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://redirect.github.com/renovatebot/renovate/discussions) if that's undesired. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/googleapis/python-datastore). --------- Co-authored-by: Anthonios Partheniou --- datastore/samples/snippets/requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datastore/samples/snippets/requirements-test.txt b/datastore/samples/snippets/requirements-test.txt index 6e6ff3ab179..2a21e952015 100644 --- a/datastore/samples/snippets/requirements-test.txt +++ b/datastore/samples/snippets/requirements-test.txt @@ -3,5 +3,5 @@ backoff==2.2.1; python_version >= "3.7" pytest===7.4.3; python_version == '3.7' pytest===8.3.5; python_version == '3.8' pytest===8.4.2; python_version == '3.9' -pytest==8.4.2; python_version >= '3.10' +pytest==9.0.2; python_version >= '3.10' flaky==3.8.1 From 1ba5e1938481272b5eccd7a2021f750da4a77e29 Mon Sep 17 00:00:00 2001 From: Jennifer Davis Date: Wed, 18 Feb 2026 18:32:58 -0800 Subject: [PATCH 51/52] fix: can't claim all rights reserved and be Apache 2. --- datastore/samples/snippets/schedule-export/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datastore/samples/snippets/schedule-export/main.py b/datastore/samples/snippets/schedule-export/main.py index 5c0eba699d0..f91b1466913 100644 --- a/datastore/samples/snippets/schedule-export/main.py +++ b/datastore/samples/snippets/schedule-export/main.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC All Rights Reserved. +# 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. From 17a1442099cc772466de4ae9802f601991c8d9f1 Mon Sep 17 00:00:00 2001 From: Jennifer Davis Date: Wed, 18 Feb 2026 22:31:50 -0800 Subject: [PATCH 52/52] fix: Update requirements-test.txt --- .../samples/snippets/schedule-export/requirements-test.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/datastore/samples/snippets/schedule-export/requirements-test.txt b/datastore/samples/snippets/schedule-export/requirements-test.txt index a664595f136..cb982446b31 100644 --- a/datastore/samples/snippets/schedule-export/requirements-test.txt +++ b/datastore/samples/snippets/schedule-export/requirements-test.txt @@ -1,4 +1,2 @@ -pytest===7.4.3; python_version == '3.7' -pytest===8.3.5; python_version == '3.8' pytest===8.4.2; python_version == '3.9' pytest==9.0.2; python_version >= '3.10'