This is an automated email from the ASF dual-hosted git repository.
jrmccluskey pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/beam.git
The following commit(s) were added to refs/heads/master by this push:
new 8c100dd7fe4 Replace typing.ByteString with collections.abc.Buffer
(#40554)
8c100dd7fe4 is described below
commit 8c100dd7fe44ede467baad886181b4df117cf219
Author: Jack McCluskey <[email protected]>
AuthorDate: Thu Oct 15 10:07:28 2026 -0400
Replace typing.ByteString with collections.abc.Buffer (#40554)
* Replace typing.ByteString with collections.abc.Buffer
* yapf
* Replace ByteString in coders_property_based_test.py
* update snowflakeio test to use buffer
* Swap BQ schema tests to collections.abc.Sequence
* corresponding change
* missing import
---
.../coders/coders_property_based_test.py | 7 ++-
.../io/external/xlang_snowflakeio_it_test.py | 8 +++-
.../apache_beam/io/gcp/bigquery_schema_tools.py | 2 +-
.../io/gcp/bigquery_schema_tools_test.py | 11 ++---
sdks/python/apache_beam/typehints/row_type.py | 30 ++++++-------
sdks/python/apache_beam/typehints/schemas.py | 50 +++++++++++-----------
sdks/python/apache_beam/typehints/schemas_test.py | 29 +++++++------
.../apache_beam/typehints/testing/strategies.py | 4 +-
8 files changed, 77 insertions(+), 64 deletions(-)
diff --git a/sdks/python/apache_beam/coders/coders_property_based_test.py
b/sdks/python/apache_beam/coders/coders_property_based_test.py
index 9b5600d7156..aa14a178745 100644
--- a/sdks/python/apache_beam/coders/coders_property_based_test.py
+++ b/sdks/python/apache_beam/coders/coders_property_based_test.py
@@ -48,10 +48,15 @@ from apache_beam.typehints.schemas import
PRIMITIVE_TO_ATOMIC_TYPE
from apache_beam.typehints.schemas import typing_to_runner_api
from apache_beam.utils.timestamp import Timestamp
+try:
+ from collections.abc import Buffer
+except ImportError:
+ from typing_extensions import Buffer
+
SCHEMA_TYPES_TO_STRATEGY = {
str: st.text(),
bytes: st.binary(),
- typing.ByteString: st.binary(),
+ Buffer: st.binary(),
# Maximum datetime on year 3000 to conform to Windows OS limits.
Timestamp: st.datetimes(
min_value=datetime(1970, 1, 1, 1, 1),
diff --git a/sdks/python/apache_beam/io/external/xlang_snowflakeio_it_test.py
b/sdks/python/apache_beam/io/external/xlang_snowflakeio_it_test.py
index 70293f863f4..f85b77e7a29 100644
--- a/sdks/python/apache_beam/io/external/xlang_snowflakeio_it_test.py
+++ b/sdks/python/apache_beam/io/external/xlang_snowflakeio_it_test.py
@@ -45,7 +45,6 @@ import argparse
import binascii
import logging
import unittest
-from typing import ByteString
from typing import NamedTuple
import apache_beam as beam
@@ -59,6 +58,11 @@ from apache_beam.testing.test_pipeline import TestPipeline
from apache_beam.testing.util import assert_that
from apache_beam.testing.util import equal_to
+try:
+ from collections.abc import Buffer
+except ImportError:
+ from typing_extensions import Buffer
+
# pylint: disable=wrong-import-order, wrong-import-position, ungrouped-imports
try:
from apache_beam.io.gcp.gcsfilesystem import GCSFileSystem
@@ -82,7 +86,7 @@ TestRow = NamedTuple(
[
('number_column', int),
('boolean_column', bool),
- ('bytes_column', ByteString),
+ ('bytes_column', Buffer),
])
coders.registry.register_coder(TestRow, coders.RowCoder)
diff --git a/sdks/python/apache_beam/io/gcp/bigquery_schema_tools.py
b/sdks/python/apache_beam/io/gcp/bigquery_schema_tools.py
index d3d608b1fc6..bd523835daa 100644
--- a/sdks/python/apache_beam/io/gcp/bigquery_schema_tools.py
+++ b/sdks/python/apache_beam/io/gcp/bigquery_schema_tools.py
@@ -22,8 +22,8 @@ NOTHING IN THIS FILE HAS BACKWARDS COMPATIBILITY GUARANTEES.
"""
import datetime
+from collections.abc import Sequence
from typing import Optional
-from typing import Sequence
import numpy as np
diff --git a/sdks/python/apache_beam/io/gcp/bigquery_schema_tools_test.py
b/sdks/python/apache_beam/io/gcp/bigquery_schema_tools_test.py
index 73cedb3a6ae..788f872ced6 100644
--- a/sdks/python/apache_beam/io/gcp/bigquery_schema_tools_test.py
+++ b/sdks/python/apache_beam/io/gcp/bigquery_schema_tools_test.py
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import collections
import logging
import typing
import unittest.mock
@@ -50,7 +51,7 @@ class TestBigQueryToSchema(unittest.TestCase):
usertype.__annotations__,
{
'stn': typing.Optional[str],
- 'temp': typing.Sequence[np.float64],
+ 'temp': collections.abc.Sequence[np.float64],
'count': typing.Optional[np.int64]
})
@@ -158,7 +159,7 @@ class TestBigQueryToSchema(unittest.TestCase):
usertype.__annotations__,
{
'stn': typing.Optional[str],
- 'temp': typing.Sequence[np.float64],
+ 'temp': collections.abc.Sequence[np.float64],
'times': typing.Optional[apache_beam.utils.timestamp.Timestamp]
})
@@ -293,7 +294,7 @@ class TestBigQueryToSchema(unittest.TestCase):
expected_annotations = {
'location': typing.Optional[str],
- 'locations': typing.Sequence[str],
+ 'locations': collections.abc.Sequence[str],
'required_location': str
}
@@ -320,7 +321,7 @@ class TestBigQueryToSchema(unittest.TestCase):
# Test repeated GEOGRAPHY field
result = bq_field_to_type("GEOGRAPHY", "REPEATED")
- self.assertEqual(result, typing.Sequence[str])
+ self.assertEqual(result, collections.abc.Sequence[str])
# Test GEOGRAPHY field with None mode (should default to nullable)
result = bq_field_to_type("GEOGRAPHY", None)
@@ -425,7 +426,7 @@ class TestTypeOverridesSchemaTools(unittest.TestCase):
typing.Optional[datetime.date])
self.assertEqual(
bq_field_to_type("DATE", "REPEATED", overrides),
- typing.Sequence[datetime.date])
+ collections.abc.Sequence[datetime.date])
def test_bq_field_to_type_overrides_can_use_str(self):
"""Test that type_overrides can map DATE/DATETIME/JSON to str."""
diff --git a/sdks/python/apache_beam/typehints/row_type.py
b/sdks/python/apache_beam/typehints/row_type.py
index 5e0faf91f55..89e15b5df82 100644
--- a/sdks/python/apache_beam/typehints/row_type.py
+++ b/sdks/python/apache_beam/typehints/row_type.py
@@ -21,11 +21,9 @@
from __future__ import annotations
import dataclasses
+from collections.abc import Sequence
from typing import Any
-from typing import Dict
from typing import Optional
-from typing import Sequence
-from typing import Tuple
from apache_beam.typehints import typehints
from apache_beam.typehints.native_type_compatibility import
match_dataclass_for_row
@@ -49,11 +47,11 @@ def _user_type_is_generated(user_type: type) -> bool:
class RowTypeConstraint(typehints.TypeConstraint):
def __init__(
self,
- fields: Sequence[Tuple[str, type]],
+ fields: Sequence[tuple[str, type]],
user_type,
- schema_options: Optional[Sequence[Tuple[str, Any]]] = None,
- field_options: Optional[Dict[str, Sequence[Tuple[str, Any]]]] = None,
- field_descriptions: Optional[Dict[str, str]] = None):
+ schema_options: Optional[Sequence[tuple[str, Any]]] = None,
+ field_options: Optional[dict[str, Sequence[tuple[str, Any]]]] = None,
+ field_descriptions: Optional[dict[str, str]] = None):
"""For internal use only, no backwards comatibility guaratees. See
https://beam.apache.org/documentation/programming-guide/#schemas-for-pl-types
for guidance on creating PCollections with inferred schemas.
@@ -103,8 +101,8 @@ class RowTypeConstraint(typehints.TypeConstraint):
@staticmethod
def from_user_type(
user_type: type,
- schema_options: Optional[Sequence[Tuple[str, Any]]] = None,
- field_options: Optional[Dict[str, Sequence[Tuple[str, Any]]]] = None
+ schema_options: Optional[Sequence[tuple[str, Any]]] = None,
+ field_options: Optional[dict[str, Sequence[tuple[str, Any]]]] = None
) -> Optional[RowTypeConstraint]:
if match_is_named_tuple(user_type):
fields = [(name, user_type.__annotations__[name])
@@ -136,12 +134,12 @@ class RowTypeConstraint(typehints.TypeConstraint):
@staticmethod
def from_fields(
- fields: Sequence[Tuple[str, type]],
+ fields: Sequence[tuple[str, type]],
schema_id: Optional[str] = None,
- schema_options: Optional[Sequence[Tuple[str, Any]]] = None,
- field_options: Optional[Dict[str, Sequence[Tuple[str, Any]]]] = None,
+ schema_options: Optional[Sequence[tuple[str, Any]]] = None,
+ field_options: Optional[dict[str, Sequence[tuple[str, Any]]]] = None,
schema_registry: Optional[SchemaTypeRegistry] = None,
- field_descriptions: Optional[Dict[str, str]] = None,
+ field_descriptions: Optional[dict[str, str]] = None,
) -> RowTypeConstraint:
return GeneratedClassRowTypeConstraint(
fields,
@@ -218,10 +216,10 @@ class GeneratedClassRowTypeConstraint(RowTypeConstraint):
self,
fields,
schema_id: Optional[str] = None,
- schema_options: Optional[Sequence[Tuple[str, Any]]] = None,
- field_options: Optional[Dict[str, Sequence[Tuple[str, Any]]]] = None,
+ schema_options: Optional[Sequence[tuple[str, Any]]] = None,
+ field_options: Optional[dict[str, Sequence[tuple[str, Any]]]] = None,
schema_registry: Optional[SchemaTypeRegistry] = None,
- field_descriptions: Optional[Dict[str, str]] = None,
+ field_descriptions: Optional[dict[str, str]] = None,
):
from apache_beam.typehints.schemas import named_fields_to_schema
from apache_beam.typehints.schemas import named_tuple_from_schema
diff --git a/sdks/python/apache_beam/typehints/schemas.py
b/sdks/python/apache_beam/typehints/schemas.py
index 9caccc762f7..a023d4f4cd6 100644
--- a/sdks/python/apache_beam/typehints/schemas.py
+++ b/sdks/python/apache_beam/typehints/schemas.py
@@ -32,13 +32,13 @@ Imposes a mapping between common Python types and Beam
portable schemas
bool <-----> BOOLEAN
str <-----> STRING
bytes <-----> BYTES
- ByteString ------> BYTES
+ Buffer ------> BYTES
Timestamp <-----> LogicalType(urn="beam:logical_type:micros_instant:v1")
datetime.date <-----> LogicalType(urn="beam:logical_type:date:v1")
datetime.time <-----> LogicalType(urn="beam:logical_type:time:v1")
Decimal <-----> LogicalType(urn="beam:logical_type:fixed_decimal:v1")
- Tuple[T, ...] <-----> LogicalType(urn="beam:logical_type:var_tuple:v1")
- Tuple[T1, T2] <-----> LogicalType(urn="beam:logical_type:fixed_tuple:v1")
+ tuple[T, ...] <-----> LogicalType(urn="beam:logical_type:var_tuple:v1")
+ tuple[T1, T2] <-----> LogicalType(urn="beam:logical_type:fixed_tuple:v1")
Mapping <-----> MapType
Sequence <-----> ArrayType
NamedTuple <-----> RowType
@@ -75,17 +75,13 @@ import dataclasses
import datetime
import decimal
import logging
+from collections.abc import Iterable
+from collections.abc import Mapping
+from collections.abc import Sequence
from typing import Any
-from typing import ByteString
-from typing import Dict
from typing import Generic
-from typing import Iterable
-from typing import List
-from typing import Mapping
from typing import NamedTuple
from typing import Optional
-from typing import Sequence
-from typing import Tuple
from typing import TypeVar
from typing import Union
@@ -110,6 +106,12 @@ from apache_beam.utils import proto_utils
from apache_beam.utils.python_callable import PythonCallableWithSource
from apache_beam.utils.timestamp import Timestamp
+# Simplify to collections.abc implementation once Python 3.11 is deprecated.
+try:
+ from collections.abc import Buffer
+except ImportError:
+ from typing_extensions import Buffer
+
PYTHON_ANY_URN = "beam:logical:pythonsdk_any:v1"
_PYTHON_ANY_FIELD_TYPE_BYTE = "_pythonsdk_any_type_byte"
_PYTHON_ANY_FIELD_PAYLOAD = "payload"
@@ -135,7 +137,7 @@ ATOMIC_TYPE_TO_PRIMITIVE = dict((atomic, typ) for typ,
atomic in _PRIMITIVES)
# One-way mappings
PRIMITIVE_TO_ATOMIC_TYPE.update({
- ByteString: schema_pb2.BYTES,
+ Buffer: schema_pb2.BYTES,
# Allow users to specify a native int, and use INT64 as the cross-language
# representation. Technically ints have unlimited precision, but RowCoder
# should throw an error if it sees one with a bit width > 64 when encoding.
@@ -150,12 +152,12 @@ _SCHEMA_ID_CACHE = {}
def named_fields_to_schema(
- names_and_types: Union[Dict[str, type], Sequence[Tuple[str, type]]],
+ names_and_types: Union[dict[str, type], Sequence[tuple[str, type]]],
schema_id: Optional[str] = None,
- schema_options: Optional[Sequence[Tuple[str, Any]]] = None,
- field_options: Optional[Dict[str, Sequence[Tuple[str, Any]]]] = None,
+ schema_options: Optional[Sequence[tuple[str, Any]]] = None,
+ field_options: Optional[dict[str, Sequence[tuple[str, Any]]]] = None,
schema_registry: SchemaTypeRegistry = SCHEMA_REGISTRY,
- field_descriptions: Optional[Dict[str, str]] = None,
+ field_descriptions: Optional[dict[str, str]] = None,
):
schema_options = schema_options or []
field_options = field_options or {}
@@ -201,7 +203,7 @@ def named_fields_to_schema(
def named_fields_from_schema(
- schema): # (schema_pb2.Schema) -> typing.List[typing.Tuple[str, type]]
+ schema): # (schema_pb2.Schema) -> list[typing.tuple[str, type]]
return [(field.name, typing_from_runner_api(field.type))
for field in schema.fields]
@@ -241,7 +243,7 @@ def value_from_runner_api(
def option_to_runner_api(
- option: Tuple[str, Any],
+ option: tuple[str, Any],
schema_registry: SchemaTypeRegistry = SCHEMA_REGISTRY) ->
schema_pb2.Option:
return SchemaTranslation(
schema_registry=schema_registry).option_to_runner_api(option)
@@ -249,7 +251,7 @@ def option_to_runner_api(
def option_from_runner_api(
option_proto: schema_pb2.Option,
- schema_registry: SchemaTypeRegistry = SCHEMA_REGISTRY) -> Tuple[str, Any]:
+ schema_registry: SchemaTypeRegistry = SCHEMA_REGISTRY) -> tuple[str, Any]:
return SchemaTranslation(
schema_registry=schema_registry).option_from_runner_api(option_proto)
@@ -541,14 +543,14 @@ class SchemaTranslation(object):
f"{typing_proto!r}.")
def option_from_runner_api(
- self, option_proto: schema_pb2.Option) -> Tuple[str, Any]:
+ self, option_proto: schema_pb2.Option) -> tuple[str, Any]:
if not option_proto.HasField('type'):
return option_proto.name, None
value = self.value_from_runner_api(option_proto.type, option_proto.value)
return option_proto.name, value
- def option_to_runner_api(self, option: Tuple[str, Any]) -> schema_pb2.Option:
+ def option_to_runner_api(self, option: tuple[str, Any]) -> schema_pb2.Option:
name, value = option
if value is None:
@@ -751,7 +753,7 @@ def schema_from_element_type(element_type: type) ->
schema_pb2.Schema:
def named_fields_from_element_type(
- element_type: type) -> List[Tuple[str, type]]:
+ element_type: type) -> list[tuple[str, type]]:
return named_fields_from_schema(schema_from_element_type(element_type))
@@ -1558,7 +1560,7 @@ class JdbcTimeType(LogicalType[datetime.time,
MillisInstant, str]):
return cls()
-_TUPLE_NAMEDTUPLE_CACHE: Dict[int, type] = {}
+_TUPLE_NAMEDTUPLE_CACHE: dict[int, type] = {}
def _get_tuple_namedtuple(n: int) -> type:
@@ -1583,7 +1585,7 @@ class FixedTupleLogicalType(NoArgumentLogicalType[tuple,
Any]):
def language_type(self=None):
if self is None or not self._tuple_types:
return tuple
- return Tuple[self._tuple_types]
+ return tuple[self._tuple_types]
def representation_type(self):
if not self._tuple_types:
@@ -1628,7 +1630,7 @@ class VarTupleLogicalType(NoArgumentLogicalType[tuple,
Sequence]):
def language_type(self=None):
if self is None or self._elem_type is object:
return tuple
- return Tuple[self._elem_type, ...]
+ return tuple[self._elem_type, ...]
def representation_type(self):
return Sequence[self._elem_type]
diff --git a/sdks/python/apache_beam/typehints/schemas_test.py
b/sdks/python/apache_beam/typehints/schemas_test.py
index 5c202a3aa3d..a41df70c5a8 100644
--- a/sdks/python/apache_beam/typehints/schemas_test.py
+++ b/sdks/python/apache_beam/typehints/schemas_test.py
@@ -25,14 +25,11 @@ import datetime
import itertools
import pickle
import unittest
+from collections.abc import Mapping
+from collections.abc import Sequence
from typing import Any
-from typing import ByteString
-from typing import List
-from typing import Mapping
from typing import NamedTuple
from typing import Optional
-from typing import Sequence
-from typing import Tuple
import numpy as np
import pytest
@@ -57,6 +54,12 @@ from apache_beam.typehints.schemas import
typing_to_runner_api
from apache_beam.typehints.testing.strategies import named_fields
from apache_beam.utils.timestamp import Timestamp
+# Simplify to collections.abc implementation once Python 3.11 is deprecated.
+try:
+ from collections.abc import Buffer
+except ImportError:
+ from typing_extensions import Buffer
+
all_nonoptional_primitives = [
np.int8,
np.int16,
@@ -81,8 +84,8 @@ basic_map_types = [
]
basic_tuple_types = [
- Tuple[str, np.int64],
- Tuple[np.int64, ...],
+ tuple[str, np.int64],
+ tuple[np.int64, ...],
]
@@ -115,8 +118,8 @@ class ComplexSchema(NamedTuple):
array_optional: Sequence[Optional[bool]]
timestamp: Timestamp
date: datetime.date
- fixed_tuple: Tuple[str, np.int64]
- var_tuple: Tuple[np.int64, ...]
+ fixed_tuple: tuple[str, np.int64]
+ var_tuple: tuple[np.int64, ...]
def get_test_beam_fieldtype_protos():
@@ -667,9 +670,9 @@ class SchemaTest(unittest.TestCase):
[
('name', str),
('age', Optional[int]),
- ('interests', List[str]),
+ ('interests', list[str]),
('height', float),
- ('blob', ByteString),
+ ('blob', Buffer),
])
expected = schema_pb2.FieldType(
@@ -712,9 +715,9 @@ class SchemaTest(unittest.TestCase):
class MyCuteDataclass:
name: str
age: Optional[int]
- interests: List[str]
+ interests: list[str]
height: float
- blob: ByteString
+ blob: Buffer
expected = schema_pb2.FieldType(
row_type=schema_pb2.RowType(
diff --git a/sdks/python/apache_beam/typehints/testing/strategies.py
b/sdks/python/apache_beam/typehints/testing/strategies.py
index a388e25eea2..97afc16d737 100644
--- a/sdks/python/apache_beam/typehints/testing/strategies.py
+++ b/sdks/python/apache_beam/typehints/testing/strategies.py
@@ -21,9 +21,9 @@ Intended for internal use only, no backward-compatibility
guarantees."""
import keyword
import unicodedata
-from typing import Mapping
+from collections.abc import Mapping
+from collections.abc import Sequence
from typing import Optional
-from typing import Sequence
from hypothesis import strategies as st