Spenserrrr commented on code in PR #58729:
URL: https://github.com/apache/spark/pull/58729#discussion_r4022083086


##########
python/pyspark/sql/eval_handlers/_base.py:
##########
@@ -0,0 +1,129 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""Base classes and registry for the eval type handlers.
+
+Kept in a leaf module (importing only ``_typing`` and serializers) so both the
+package ``__init__`` and the concrete-handler submodules can import it without 
a
+circular import.
+"""
+
+from abc import ABCMeta, abstractmethod
+from collections.abc import Iterator
+from typing import TYPE_CHECKING, Any, ClassVar, Generic, Optional, cast
+
+from pyspark.serializers import Serializer
+from pyspark.sql.eval_handlers._typing import (
+    CoGroupedBatch,
+    GroupedBatch,
+    InputBatch,
+    OutputBatch,
+)
+from pyspark.sql.pandas.serializers import (
+    ArrowStreamCoGroupSerializer,
+    ArrowStreamGroupSerializer,
+    ArrowStreamSerializer,
+)
+
+if TYPE_CHECKING:
+    import pyarrow as pa  # noqa: F401  # only in the batch category's 
forward-ref subscript
+
+    from pyspark.worker_util import EvalConf, RunnerConf
+
+# eval type -> handler class, populated by _EvalTypeHandlerMeta at class 
definition.
+EVAL_TYPE_HANDLERS: "dict[int, type[EvalTypeHandler]]" = {}
+
+
+class _EvalTypeHandlerMeta(ABCMeta):

Review Comment:
   I'm wondering if we can pupulate this mapping explicitly instead of through 
the metaclass, given Tian suggests that we can keep this registry private. 
Right now, these handlers are interal to Spark, so there is no dynamic 
third-party discovery requirement. Also, each concrete handler module already 
has to be imported, so the metaclass is not doing discovery for us. I'm 
wondering if we can add a _registry.py file, and do something like:
   ```
   from pyspark.sql.eval_handlers._arrow import ArrowScalarUDFHandler
    
    _eval_type_handlers = {
        ArrowScalarUDFHandler.eval_type: ArrowScalarUDFHandler,
    }
    
    
    def get_eval_type_handler(eval_type):
         return _eval_type_handlers.get(eval_type)
   ```
    and in the worker.py and we call `handler_cls = 
get_eval_type_handler(eval_type)`. This can make the supported eval type 
clearer. Do you see a  case where class-time self-registration would help as 
more handlers are added?



##########
python/pyspark/worker.py:
##########
@@ -2053,6 +1881,18 @@ def _elementwise_result_to_arrow(result, return_type, 
arrow_element_type, is_pan
 
 
 def read_udfs(pickleSer, udf_info_list, eval_type, runner_conf, eval_conf):
+    # Eval types that have been migrated to a handler are dispatched here 
without
+    # walking the if/elif chain below. The handler owns the whole lifecycle,
+    # including serializer selection.
+    handler_cls = EVAL_TYPE_HANDLERS.get(eval_type)
+    if handler_cls is not None:
+        udfs = [
+            read_single_udf(pickleSer, udf_info, eval_type, runner_conf, 
udf_index=udf_index)

Review Comment:
   One suggestion for the new handler path. Do you think it is better to split 
the common UDF loading from the eval-type prepartation? Right now, 
read_single_udf does both common work such as deserialization and it also does 
eval-type-speicifc prepartion that returns different typle layouts.
   The next line passes these values through the generic handler boundary as 
list[tuple[Any, ...]], so the meaning of each position is known only by 
read_single_udf and the concrete handler. For example, can we split this 
function into two:
   ```
   loaded_udf = load_single_udf(...)                   # common work
   prepared_udf = handler_cls.prepare_udf(loaded_udf)  # eval-type-specific work
   ```
   and the scalar handler can define the shape it needs, like:
   ```
   class PreparedScalarUDF(NamedTuple):
       func: Callable[..., Any]
       args_offsets: list[int]
       kwargs_offsets: dict[str, int]
       return_type: DataType
   ```



##########
python/pyspark/sql/eval_handlers/_typing.py:
##########
@@ -0,0 +1,37 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""Type aliases and variables for the eval type handlers.
+
+The Arrow element types are forward refs so pyarrow stays a type-checking-only
+import at runtime, as elsewhere in the worker.
+"""
+
+from collections.abc import Iterator
+from typing import TYPE_CHECKING, TypeVar
+
+if TYPE_CHECKING:
+    import pyarrow as pa
+
+# Grouped-category input element, matching what the serializer yields: a group
+# serializer yields a lazy iterator; a co-group serializer materializes each 
side.
+GroupedBatch = Iterator["pa.RecordBatch"]

Review Comment:
   Do you think Iterable[RecordBatch] is better here? The group serializer 
yields an iterator normally, but pipelined_process materializes each group into 
a list before passing it to run, so a future handler relying on iterator-only 
operations could fail only with pipelining enabled. A list-backed grouped case 
would cover that representation.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to