Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
26 changes: 12 additions & 14 deletions samples/samples/archived/backup_snippet.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"""

import time
from datetime import datetime, timedelta
from datetime import datetime, timedelta, timezone

from google.cloud import spanner

Expand All @@ -30,7 +30,7 @@ def cancel_backup(instance_id, database_id, backup_id):
instance = spanner_client.instance(instance_id)
database = instance.database(database_id)

expire_time = datetime.utcnow() + timedelta(days=30)
expire_time = datetime.now(timezone.utc) + timedelta(days=30)

# Create a backup.
backup = instance.backup(backup_id, database=database, expire_time=expire_time)
Expand Down Expand Up @@ -63,14 +63,14 @@ def copy_backup(instance_id, backup_id, source_backup_path):
instance = spanner_client.instance(instance_id)

# Create a backup object and wait for copy backup operation to complete.
expire_time = datetime.utcnow() + timedelta(days=14)
expire_time = datetime.now(timezone.utc) + timedelta(days=14)
copy_backup = instance.copy_backup(
backup_id=backup_id, source_backup=source_backup_path, expire_time=expire_time
)
operation = copy_backup.create()

# Wait for copy backup operation to complete.
operation.result(2100)
operation.result(3600)

# Verify that the copy backup is ready.
copy_backup.reload()
Expand All @@ -97,14 +97,14 @@ def create_backup(instance_id, database_id, backup_id, version_time):
database = instance.database(database_id)

# Create a backup
expire_time = datetime.utcnow() + timedelta(days=14)
expire_time = datetime.now(timezone.utc) + timedelta(days=14)
backup = instance.backup(
backup_id, database=database, expire_time=expire_time, version_time=version_time
)
operation = backup.create()

# Wait for backup operation to complete.
operation.result(2100)
operation.result(3600)

# Verify that the backup is ready.
backup.reload()
Expand All @@ -127,15 +127,14 @@ def create_backup_with_encryption_key(
instance_id, database_id, backup_id, kms_key_name
):
"""Creates a backup for a database using a Customer Managed Encryption Key (CMEK)."""
from google.cloud.spanner_admin_database_v1 import \
CreateBackupEncryptionConfig
from google.cloud.spanner_admin_database_v1 import CreateBackupEncryptionConfig

spanner_client = spanner.Client()
instance = spanner_client.instance(instance_id)
database = instance.database(database_id)

# Create a backup
expire_time = datetime.utcnow() + timedelta(days=14)
expire_time = datetime.now(timezone.utc) + timedelta(days=14)
encryption_config = {
"encryption_type": CreateBackupEncryptionConfig.EncryptionType.CUSTOMER_MANAGED_ENCRYPTION,
"kms_key_name": kms_key_name,
Expand All @@ -149,7 +148,7 @@ def create_backup_with_encryption_key(
operation = backup.create()

# Wait for backup operation to complete.
operation.result(2100)
operation.result(3600)

# Verify that the backup is ready.
backup.reload()
Expand Down Expand Up @@ -295,7 +294,7 @@ def list_backups(instance_id, database_id, backup_id):
print(backup.name)

# List all backups that expire before a timestamp.
expire_time = datetime.utcnow().replace(microsecond=0) + timedelta(days=30)
expire_time = datetime.now(timezone.utc).replace(microsecond=0) + timedelta(days=30)
print(
'All backups with expire_time before "{}-{}-{}T{}:{}:{}Z":'.format(
*expire_time.timetuple()
Expand All @@ -312,7 +311,7 @@ def list_backups(instance_id, database_id, backup_id):
print(backup.name)

# List backups that were created after a timestamp that are also ready.
create_time = datetime.utcnow().replace(microsecond=0) - timedelta(days=1)
create_time = datetime.now(timezone.utc).replace(microsecond=0) - timedelta(days=1)
print(
'All backups created after "{}-{}-{}T{}:{}:{}Z" and are READY:'.format(
*create_time.timetuple()
Expand Down Expand Up @@ -396,8 +395,7 @@ def restore_database_with_encryption_key(
instance_id, new_database_id, backup_id, kms_key_name
):
"""Restores a database from a backup using a Customer Managed Encryption Key (CMEK)."""
from google.cloud.spanner_admin_database_v1 import \
RestoreDatabaseEncryptionConfig
from google.cloud.spanner_admin_database_v1 import RestoreDatabaseEncryptionConfig

spanner_client = spanner.Client()
instance = spanner_client.instance(instance_id)
Expand Down
Loading
Loading