Repository: googleapis/python-bigquery
Pull request #2110 (2025-01-14 13:14:22)

feat: Adds ForeignTypeInfo class and tests
This PR adds the `ForeignTypeInfo` class and the associated tests, plus minor tweaks to support both of those changes.

Also incorporates several small changes to correct some docstrings in other PANGEA classes for more uniformity across the PANGEA code additions.



Patch:
diff --git a/google/cloud/bigquery/schema.py b/google/cloud/bigquery/schema.py
index 8d62b2b5..b278b686 100644
--- a/google/cloud/bigquery/schema.py
+++ b/google/cloud/bigquery/schema.py
@@ -560,6 +560,63 @@ class PolicyTagList(object):
         return answer
 
 
+class ForeignTypeInfo:
+    """Metadata about the foreign data type definition such as the system in which the
+    type is defined.
+
+    Args:
+        type_system (str): Required. Specifies the system which defines the
+            foreign data type.
+
+            TypeSystem enum currently includes:
+            * "TYPE_SYSTEM_UNSPECIFIED"
+            * "HIVE"
+    """
+
+    def __init__(self, type_system: Optional[str] = None):
+        self._properties: Dict[str, Any] = {}
+        self.type_system = type_system
+
+    @property
+    def type_system(self) -> Optional[str]:
+        """Required. Specifies the system which defines the foreign data
+        type."""
+
+        return self._properties.get("typeSystem")
+
+    @type_system.setter
+    def type_system(self, value: Optional[str]):
+        value = _helpers._isinstance_or_raise(value, str, none_allowed=True)
+        self._properties["typeSystem"] = value
+
+    def to_api_repr(self) -> dict:
+        """Build an API representation of this object.
+
+        Returns:
+            Dict[str, Any]:
+                A dictionary in the format used by the BigQuery API.
+        """
+
+        return self._properties
+
+    @classmethod
+    def from_api_repr(cls, api_repr: Dict[str, Any]) -> "ForeignTypeInfo":
+        """Factory: constructs an instance of the class (cls)
+        given its API representation.
+
+        Args:
+            api_repr (Dict[str, Any]):
+                API representation of the object to be instantiated.
+
+        Returns:
+            An instance of the class initialized with data from 'api_repr'.
+        """
+
+        config = cls()
+        config._properties = api_repr
+        return config
+
+
 class SerDeInfo:
     """Serializer and deserializer information.
 
@@ -625,6 +682,7 @@ class SerDeInfo:
 
     def to_api_repr(self) -> dict:
         """Build an API representation of this object.
+
         Returns:
             Dict[str, Any]:
                 A dictionary in the format used by the BigQuery API.
@@ -635,11 +693,13 @@ class SerDeInfo:
     def from_api_repr(cls, api_repr: dict) -> SerDeInfo:
         """Factory: constructs an instance of the class (cls)
         given its API representation.
+
         Args:
-            resource (Dict[str, Any]):
+            api_repr (Dict[str, Any]):
                 API representation of the object to be instantiated.
+
         Returns:
-            An instance of the class initialized with data from 'resource'.
+            An instance of the class initialized with data from 'api_repr'.
         """
         config = cls("PLACEHOLDER")
         config._properties = api_repr


Test patch:
diff --git a/tests/unit/test_schema.py b/tests/unit/test_schema.py
index 7e84dd63..efbc5d26 100644
--- a/tests/unit/test_schema.py
+++ b/tests/unit/test_schema.py
@@ -1128,6 +1128,73 @@ def test_to_api_repr_parameterized(field, api):
     assert SchemaField(**field).to_api_repr() == api
 
 
+class TestForeignTypeInfo:
+    """Tests for ForeignTypeInfo objects."""
+
+    @staticmethod
+    def _get_target_class():
+        from google.cloud.bigquery.schema import ForeignTypeInfo
+
+        return ForeignTypeInfo
+
+    def _make_one(self, *args, **kw):
+        return self._get_target_class()(*args, **kw)
+
+    @pytest.mark.parametrize(
+        "type_system,expected",
+        [
+            (None, None),
+            ("TYPE_SYSTEM_UNSPECIFIED", "TYPE_SYSTEM_UNSPECIFIED"),
+            ("HIVE", "HIVE"),
+        ],
+    )
+    def test_ctor_valid_input(self, type_system, expected):
+        result = self._make_one(type_system=type_system)
+
+        assert result.type_system == expected
+
+    def test_ctor_invalid_input(self):
+        with pytest.raises(TypeError) as e:
+            self._make_one(type_system=123)
+
+        # Looking for the first word from the string "Pass <variable> as..."
+        assert "Pass " in str(e.value)
+
+    @pytest.mark.parametrize(
+        "type_system,expected",
+        [
+            ("TYPE_SYSTEM_UNSPECIFIED", {"typeSystem": "TYPE_SYSTEM_UNSPECIFIED"}),
+            ("HIVE", {"typeSystem": "HIVE"}),
+            (None, {"typeSystem": None}),
+        ],
+    )
+    def test_to_api_repr(self, type_system, expected):
+        result = self._make_one(type_system=type_system)
+
+        assert result.to_api_repr() == expected
+
+    def test_from_api_repr(self):
+        """GIVEN an api representation of a ForeignTypeInfo object (i.e. api_repr)
+        WHEN converted into a ForeignTypeInfo object using from_api_repr()
+        THEN it will have the same representation in dict format as a ForeignTypeInfo
+        object made directly (via _make_one()) and represented in dict format.
+        """
+        api_repr = {
+            "typeSystem": "TYPE_SYSTEM_UNSPECIFIED",
+        }
+
+        expected = self._make_one(
+            type_system="TYPE_SYSTEM_UNSPECIFIED",
+        )
+
+        klass = self._get_target_class()
+        result = klass.from_api_repr(api_repr)
+
+        # We convert both to dict format because these classes do not have a
+        # __eq__() method to facilitate direct equality comparisons.
+        assert result.to_api_repr() == expected.to_api_repr()
+
+
 class TestSerDeInfo:
     """Tests for the SerDeInfo class."""
 
@@ -1190,9 +1257,9 @@ class TestSerDeInfo:
         assert serde_info.to_api_repr() == expected_repr
 
     def test_from_api_repr(self):
-        """GIVEN an api representation of a SerDeInfo object (i.e. resource)
+        """GIVEN an api representation of a SerDeInfo object (i.e. api_repr)
         WHEN converted into a SerDeInfo object using from_api_repr()
-        THEN it will have the representation in dict format as a SerDeInfo
+        THEN it will have the same representation in dict format as a SerDeInfo
         object made directly (via _make_one()) and represented in dict format.
         """
         api_repr = {