Repository: googleapis/python-bigquery
Pull request #2087 (2024-12-05 02:28:49)

feat: add property for maxStaleness in table definitions
Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly:
- [x] Make sure to open an issue as a [bug/issue](https://github.com/googleapis/python-bigquery/issues/new/choose) before writing your code!  That way we can discuss the change, evaluate designs, and agree on the general idea
- [x] Ensure the tests and linter pass
- [ ] Code coverage does not decrease (if any source code was changed)
- [ ] Appropriate docs were updated (if necessary)

Fixes https://github.com/googleapis/python-bigquery/issues/2086 🦕

## Reference
- https://cloud.google.com/bigquery/docs/reference/rest/v2/tables#resource:-table



Patch:
diff --git a/google/cloud/bigquery/table.py b/google/cloud/bigquery/table.py
index 38542023..80ab330b 100644
--- a/google/cloud/bigquery/table.py
+++ b/google/cloud/bigquery/table.py
@@ -407,6 +407,7 @@ class Table(_TableBase):
         "view_query": "view",
         "require_partition_filter": "requirePartitionFilter",
         "table_constraints": "tableConstraints",
+        "max_staleness": "maxStaleness",
     }
 
     def __init__(self, table_ref, schema=None) -> None:
@@ -1115,6 +1116,40 @@ class Table(_TableBase):
     def __str__(self):
         return f"{self.project}.{self.dataset_id}.{self.table_id}"
 
+    @property
+    def max_staleness(self):
+        """Union[str, None]: The maximum staleness of data that could be returned when the table is queried.
+
+        Staleness encoded as a string encoding of sql IntervalValue type.
+        This property is optional and defaults to None.
+
+        According to the BigQuery API documentation, maxStaleness specifies the maximum time
+        interval for which stale data can be returned when querying the table.
+        It helps control data freshness in scenarios like metadata-cached external tables.
+
+        Returns:
+            Optional[str]: A string representing the maximum staleness interval
+            (e.g., '1h', '30m', '15s' for hours, minutes, seconds respectively).
+        """
+        return self._properties.get(self._PROPERTY_TO_API_FIELD["max_staleness"])
+
+    @max_staleness.setter
+    def max_staleness(self, value):
+        """Set the maximum staleness for the table.
+
+        Args:
+            value (Optional[str]): A string representing the maximum staleness interval.
+                Must be a valid time interval string.
+                Examples include '1h' (1 hour), '30m' (30 minutes), '15s' (15 seconds).
+
+        Raises:
+            ValueError: If the value is not None and not a string.
+        """
+        if value is not None and not isinstance(value, str):
+            raise ValueError("max_staleness must be a string or None")
+
+        self._properties[self._PROPERTY_TO_API_FIELD["max_staleness"]] = value
+
 
 class TableListItem(_TableBase):
     """A read-only table resource from a list operation.


Test patch:
diff --git a/tests/unit/test_table.py b/tests/unit/test_table.py
index ff059347..3824da22 100644
--- a/tests/unit/test_table.py
+++ b/tests/unit/test_table.py
@@ -1475,6 +1475,49 @@ class TestTable(unittest.TestCase, _SchemaBase):
         table1 = self._make_one(TableReference(dataset, "table1"))
         self.assertEqual(str(table1), "project1.dataset1.table1")
 
+    def test_max_staleness_getter(self):
+        """Test getting max_staleness property."""
+        dataset = DatasetReference("test-project", "test_dataset")
+        table_ref = dataset.table("test_table")
+        table = self._make_one(table_ref)
+        # Initially None
+        self.assertIsNone(table.max_staleness)
+        # Set max_staleness using setter
+        table.max_staleness = "1h"
+        self.assertEqual(table.max_staleness, "1h")
+
+    def test_max_staleness_setter(self):
+        """Test setting max_staleness property."""
+        dataset = DatasetReference("test-project", "test_dataset")
+        table_ref = dataset.table("test_table")
+        table = self._make_one(table_ref)
+        # Set valid max_staleness
+        table.max_staleness = "30m"
+        self.assertEqual(table.max_staleness, "30m")
+        # Set to None
+        table.max_staleness = None
+        self.assertIsNone(table.max_staleness)
+
+    def test_max_staleness_setter_invalid_type(self):
+        """Test setting max_staleness with an invalid type raises ValueError."""
+        dataset = DatasetReference("test-project", "test_dataset")
+        table_ref = dataset.table("test_table")
+        table = self._make_one(table_ref)
+        # Try setting invalid type
+        with self.assertRaises(ValueError):
+            table.max_staleness = 123  # Not a string
+
+    def test_max_staleness_to_api_repr(self):
+        """Test max_staleness is correctly represented in API representation."""
+        dataset = DatasetReference("test-project", "test_dataset")
+        table_ref = dataset.table("test_table")
+        table = self._make_one(table_ref)
+        # Set max_staleness
+        table.max_staleness = "1h"
+        # Convert to API representation
+        resource = table.to_api_repr()
+        self.assertEqual(resource.get("maxStaleness"), "1h")
+
 
 class Test_row_from_mapping(unittest.TestCase, _SchemaBase):
     PROJECT = "prahj-ekt"