Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
13fca93
fix: move return out of finally block in cursor.py
aseering Feb 27, 2026
2d08ad3
chore: replace utcnow() with now(timezone.utc) and fix test collection
aseering Feb 27, 2026
7982c32
fix: ensure required labels are present in metrics exporter
aseering Feb 27, 2026
a6c1bf4
build: drop support for Python 3.9
aseering Feb 27, 2026
4971e59
merge remote/main into fix/test-failures and resolve conflicts
aseering Feb 27, 2026
e3ba501
build: remove obsolete python3.9 sample tests directory
aseering Feb 27, 2026
7e793cc
build: use generic python3 in sample scripts instead of python3.10
aseering Feb 27, 2026
e625c96
build: comprehensive removal of Python 3.9 support
aseering Feb 27, 2026
eb5595b
fix: resolve subsequent test failures from refactoring
aseering Feb 27, 2026
fda030c
build: restore DEFAULT_PYTHON_VERSION and SYSTEM_TEST_PYTHON_VERSIONS…
aseering Feb 27, 2026
737c69e
chore: run black to fix lint errors and make flaky test more resilient
aseering Feb 28, 2026
ef7eff8
Fix utcnow deprecations in samples and improve backup test reliability
aseering Feb 28, 2026
03ca3d9
Fix flake8 and skip flaky concurrent tests
aseering Feb 28, 2026
94dcc23
Fix flake8 and unit test run by adding missing pytest import and runn…
aseering Feb 28, 2026
8cd7f05
Fix flake8: Remove unused unittest import in test_spanner.py
aseering Feb 28, 2026
49066f1
Fix backup test hangs and teardown failures
aseering Feb 28, 2026
fa73c8e
Safeguard cancel_backup loops with timeouts to prevent infinite hangs
aseering Feb 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/presubmit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
python: ["3.10", "3.11", "3.12", "3.13", "3.14"]

steps:
- name: Checkout code
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# Only run a subset of all nox sessions
env_vars: {
key: "NOX_SESSION"
value: "unit-3.9 unit-3.14 system-3.14"
value: "unit-3.14 system-3.14"
}

env_vars: {
Expand Down
40 changes: 0 additions & 40 deletions .kokoro/samples/python3.9/common.cfg

This file was deleted.

6 changes: 0 additions & 6 deletions .kokoro/samples/python3.9/continuous.cfg

This file was deleted.

11 changes: 0 additions & 11 deletions .kokoro/samples/python3.9/periodic-head.cfg

This file was deleted.

6 changes: 0 additions & 6 deletions .kokoro/samples/python3.9/periodic.cfg

This file was deleted.

6 changes: 0 additions & 6 deletions .kokoro/samples/python3.9/presubmit.cfg

This file was deleted.

4 changes: 2 additions & 2 deletions .kokoro/test-samples-impl.sh
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export PYTHONUNBUFFERED=1
env | grep KOKORO

# Install nox
python3.9 -m pip install --upgrade --quiet nox
python3 -m pip install --upgrade --quiet nox

# Use secrets acessor service account to get secrets
if [[ -f "${KOKORO_GFILE_DIR}/secrets_viewer_service_account.json" ]]; then
Expand Down Expand Up @@ -88,7 +88,7 @@ for file in samples/**/requirements.txt; do
echo "------------------------------------------------------------"

# Use nox to execute the tests for the project.
python3.9 -m nox -s "$RUN_TESTS_SESSION"
python3 -m nox -s "$RUN_TESTS_SESSION"
EXIT=$?

# If this is a periodic build, send the test log to the FlakyBot.
Expand Down
2 changes: 1 addition & 1 deletion .librarian/generator-input/librarian.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@
split_system_tests=True,
system_test_extras=["tracing"],
system_test_python_versions=["3.12"],
unit_test_python_versions=["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
unit_test_python_versions=["3.10", "3.11", "3.12", "3.13", "3.14"]
)
s.move(
templated_files,
Expand Down
3 changes: 1 addition & 2 deletions .librarian/generator-input/noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,12 @@
ISORT_VERSION = "isort==5.11.0"
LINT_PATHS = ["google", "tests", "noxfile.py", "setup.py"]

DEFAULT_PYTHON_VERSION = "3.14"
DEFAULT_PYTHON_VERSION = "3.10"

DEFAULT_MOCK_SERVER_TESTS_PYTHON_VERSION = "3.12"
SYSTEM_TEST_PYTHON_VERSIONS: List[str] = ["3.14"]

UNIT_TEST_PYTHON_VERSIONS: List[str] = [
"3.9",
"3.10",
"3.11",
"3.12",
Expand Down
3 changes: 1 addition & 2 deletions .librarian/generator-input/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@
"License :: OSI Approved :: Apache Software License",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
Expand All @@ -95,7 +94,7 @@
packages=packages,
install_requires=dependencies,
extras_require=extras,
python_requires=">=3.9",
python_requires=">=3.10",
include_package_data=True,
zip_safe=False,
)
3 changes: 1 addition & 2 deletions .librarian/state.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ libraries:
- ^scripts/fixup_
- ^setup.py
- ^testing/constraints-3.8
- ^testing/constraints-3.9
- ^testing/constraints-3.1
- ^testing/constraints-3.10
- ^docs/conf.py
- ^docs/_static
- ^docs/spanner_v1/types_.rst
Expand Down
16 changes: 9 additions & 7 deletions google/cloud/spanner_dbapi/cursor.py
Original file line number Diff line number Diff line change
Expand Up @@ -505,15 +505,17 @@ def _fetch(self, cursor_statement_type, size=None):
raise
else:
self.transaction_helper.retry_transaction()
except Aborted as e:
exception = e
except Exception as e:
exception = e
raise
finally:
if not self._in_retry_mode:
self.transaction_helper.add_fetch_statement_for_retry(
self, rows, exception, is_fetch_all
)
return rows

if not self._in_retry_mode:
self.transaction_helper.add_fetch_statement_for_retry(
self, rows, exception, is_fetch_all
)

return rows

def _handle_DQL_with_snapshot(self, snapshot, sql, params):
self._result_set = snapshot.execute_sql(
Expand Down
26 changes: 25 additions & 1 deletion google/cloud/spanner_v1/metrics/metrics_exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
MONITORED_RESOURCE_LABELS,
METRIC_LABELS,
METRIC_NAMES,
MONITORED_RES_LABEL_KEY_PROJECT,
MONITORED_RES_LABEL_KEY_INSTANCE,
)

import logging
Expand Down Expand Up @@ -299,8 +301,8 @@ def _data_point_to_timeseries_pb(
)
return series

@staticmethod
def _resource_metrics_to_timeseries_pb(
self,
metrics_data: "MetricsData",
) -> List["TimeSeries"]:
"""
Expand All @@ -324,6 +326,28 @@ def _resource_metrics_to_timeseries_pb(
) = CloudMonitoringMetricsExporter._extract_metric_labels(
data_point
)

# Ensure project_id is present in monitored resource labels
if (
MONITORED_RES_LABEL_KEY_PROJECT
not in monitored_resource_labels
):
monitored_resource_labels[
MONITORED_RES_LABEL_KEY_PROJECT
] = self.project_id

# The OpenTelemetry exporter uses the 'spanner_instance_client' resource type,
# which strictly requires both project_id and instance_id. However, some
# Spanner API calls (like creating or listing instances) operate at the
# project level and naturally lack an instance_id. We silently drop these
# metrics here to prevent Cloud Monitoring from rejecting the entire batch
# with a 400 InvalidArgument error.
if (
MONITORED_RES_LABEL_KEY_INSTANCE
not in monitored_resource_labels
):
continue

monitored_resource = CloudMonitoringMetricsExporter._resource_to_monitored_resource_pb(
resource_metric.resource, monitored_resource_labels
)
Expand Down
4 changes: 3 additions & 1 deletion google/cloud/spanner_v1/pool.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@

from google.cloud.spanner_v1.metrics.metrics_capture import MetricsCapture

_NOW = datetime.datetime.utcnow # unit tests may replace

def _NOW():
return datetime.datetime.now(datetime.timezone.utc) # unit tests may replace


class AbstractSessionPool(object):
Expand Down
4 changes: 2 additions & 2 deletions google/cloud/spanner_v1/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

from functools import total_ordering
import time
from datetime import datetime
from datetime import datetime, timezone
from typing import MutableMapping, Optional

from google.api_core.exceptions import Aborted
Expand Down Expand Up @@ -80,7 +80,7 @@ def __init__(self, database, labels=None, database_role=None, is_multiplexed=Fal
self._labels: MutableMapping[str, str] = labels
self._database_role: Optional[str] = database_role
self._is_multiplexed: bool = is_multiplexed
self._last_use_time: datetime = datetime.utcnow()
self._last_use_time: datetime = datetime.now(timezone.utc)

def __lt__(self, other):
return self._session_id < other._session_id
Expand Down
2 changes: 0 additions & 2 deletions noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@
SYSTEM_TEST_PYTHON_VERSIONS: List[str] = ["3.14"]

UNIT_TEST_PYTHON_VERSIONS: List[str] = [
"3.9",
"3.10",
"3.11",
"3.12",
Expand Down Expand Up @@ -81,7 +80,6 @@
CURRENT_DIRECTORY = pathlib.Path(__file__).parent.absolute()

nox.options.sessions = [
"unit-3.9",
"unit-3.10",
"unit-3.11",
"unit-3.12",
Expand Down
Loading
Loading