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 b4f9f3ec756 Update xgboost dependency, execute in CI (#40694)
b4f9f3ec756 is described below

commit b4f9f3ec756429215ff4361f416f89503e1240ba
Author: Jack McCluskey <[email protected]>
AuthorDate: Wed Oct 21 13:56:18 2026 -0400

    Update xgboost dependency, execute in CI (#40694)
    
    * Update xgboost dependency, execute in CI
    
    * Apply correct link to entry in CHANGES.md
    
    * Fix typos
    
    * Comment cleanup
    
    * Mark datatable handler as deprecated
---
 .../beam_PostCommit_Python_Dependency.json         |  2 +-
 .../trigger_files/beam_PreCommit_Python_ML.json    |  2 +-
 CHANGES.md                                         |  3 +++
 .../apache_beam/ml/inference/xgboost_inference.py  |  9 ++++++++-
 .../ml/inference/xgboost_inference_test.py         |  4 +++-
 .../ml/inference/xgboost_tests_requirements.txt    |  4 ++--
 sdks/python/setup.py                               | 22 ++++++++++++++++------
 sdks/python/test-suites/tox/py311/build.gradle     |  9 +++++++++
 sdks/python/tox.ini                                | 12 +++++-------
 9 files changed, 48 insertions(+), 19 deletions(-)

diff --git a/.github/trigger_files/beam_PostCommit_Python_Dependency.json 
b/.github/trigger_files/beam_PostCommit_Python_Dependency.json
index 16209484727..9bc6afbb096 100644
--- a/.github/trigger_files/beam_PostCommit_Python_Dependency.json
+++ b/.github/trigger_files/beam_PostCommit_Python_Dependency.json
@@ -1,4 +1,4 @@
 {
     "comment": "Modify this file in a trivial way to cause this test suite to 
run",
-    "modification": 4
+    "modification": 5
   }
diff --git a/.github/trigger_files/beam_PreCommit_Python_ML.json 
b/.github/trigger_files/beam_PreCommit_Python_ML.json
index 7df0105a811..2b9b913580a 100644
--- a/.github/trigger_files/beam_PreCommit_Python_ML.json
+++ b/.github/trigger_files/beam_PreCommit_Python_ML.json
@@ -1,4 +1,4 @@
 {
   "comment": "Modify this file in a trivial way to cause this test suite to 
run",
-  "revision": 22
+  "revision": 23
 }
diff --git a/CHANGES.md b/CHANGES.md
index 74dcb41af94..4e2fffb8938 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -95,10 +95,13 @@
 * (Python) `Regex.find_all` with `group=Regex.ALL` now returns every group, as 
`(match, group1, group2, ...)`, instead of only group 1, and with 
`outputEmpty=False` it drops a match only when the whole match is empty, 
matching the `group=0` behavior 
([#40399](https://github.com/apache/beam/pull/40399)).
 * (Python) `Regex.all_matches` now returns every group of the regex, instead 
of failing with a `TypeError` when the regex has no groups and dropping nested 
or unmatched trailing groups 
([#40562](https://github.com/apache/beam/issues/40562)).
 * (Python) `--dataflow_endpoint` now defaults to `''` instead of 
`https://dataflow.googleapis.com`. When unset, the Google Cloud client library 
resolves the Dataflow endpoint and honors `GOOGLE_CLOUD_UNIVERSE_DOMAIN`, so 
Dataflow jobs can be submitted in non-default universes. Code that reads 
`GoogleCloudOptions.dataflow_endpoint` should handle an empty value 
([#40569](https://github.com/apache/beam/issues/40569)).
+* (Python) The `xgboost` extras now install `xgboost>=2.0.0,<4` and 
`datatable>=1.1.0,<2`, moving both the lower and upper bounds for both. The 
previous minimum `xgboost` and `datatable` versions (`1.6.0` and `1.0.0` 
respectively) are no longer allowed 
([#40694](https://github.com/apache/beam/issues/40694)).
 
 ## Deprecations
 
 * Support for Python 3.10 was removed following its end-of-life 
([#40250](https://github.com/apache/beam/issues/40250)).
+* (Python) `XGBoostModelHandlerDatatable` is deprecated and will be removed in 
Beam 2.79.0, along with the `datatable` dependency of the `xgboost` extra. 
`datatable` has not had a release since 2023 and has no wheels for Python 
3.13+. Use `XGBoostModelHandlerPandas` (or the NumPy/SciPy handlers) instead 
([#40705](https://github.com/apache/beam/issues/40705)).
+* X behavior is deprecated and will be removed in X versions 
([#X](https://github.com/apache/beam/issues/X)).
 
 ## Bugfixes
 
diff --git a/sdks/python/apache_beam/ml/inference/xgboost_inference.py 
b/sdks/python/apache_beam/ml/inference/xgboost_inference.py
index f11b892062e..b27db8b2046 100644
--- a/sdks/python/apache_beam/ml/inference/xgboost_inference.py
+++ b/sdks/python/apache_beam/ml/inference/xgboost_inference.py
@@ -36,6 +36,7 @@ from apache_beam.ml.inference.base import ModelHandler
 from apache_beam.ml.inference.base import ModelT
 from apache_beam.ml.inference.base import PredictionResult
 from apache_beam.ml.inference.base import PredictionT
+from apache_beam.utils.annotations import deprecated
 
 __all__ = [
     'XGBoostModelHandler',
@@ -119,7 +120,7 @@ class XGBoostModelHandler(ModelHandler[ExampleT, 
PredictionT, ModelT], ABC):
         before loading the model.
 
     **Supported Versions:** RunInference APIs in Apache Beam have been tested
-    with XGBoost 1.6.0 and 1.7.0
+    with XGBoost 2.x and 3.x
 
     XGBoost 1.0.0 introduced support for using JSON to save and load
     XGBoost models. XGBoost 1.6.0, additional support for Universal Binary 
JSON.
@@ -313,6 +314,12 @@ class 
XGBoostModelHandlerSciPy(XGBoostModelHandler[scipy.sparse.csr_matrix,
     return sum(sys.getsizeof(element) for element in batch)
 
 
+@deprecated(
+    since='2.78.0',
+    extra_message=(
+        'datatable has not had a release since 2023 and has no wheels for '
+        'Python 3.13+. XGBoostModelHandlerDatatable and the datatable '
+        'dependency will be removed in Beam 2.79.0.'))
 class XGBoostModelHandlerDatatable(XGBoostModelHandler[datatable.Frame,
                                                        PredictionResult,
                                                        Union[xgboost.Booster,
diff --git a/sdks/python/apache_beam/ml/inference/xgboost_inference_test.py 
b/sdks/python/apache_beam/ml/inference/xgboost_inference_test.py
index 8e3fb77615c..e686ac5e6f2 100644
--- a/sdks/python/apache_beam/ml/inference/xgboost_inference_test.py
+++ b/sdks/python/apache_beam/ml/inference/xgboost_inference_test.py
@@ -69,7 +69,9 @@ def _compare_keyed_prediction_result(
 
 
 def predict_fn(self, data):
-  self.inference_calls += 1
+  # Models loaded from disk inside the pipeline do not carry the counter set by
+  # build_monkeypatched_xgboost_classifier().
+  self.inference_calls = getattr(self, 'inference_calls', 0) + 1
   if isinstance(data, pandas.DataFrame):
     data = data.to_numpy()
   if isinstance(data, datatable.Frame):
diff --git 
a/sdks/python/apache_beam/ml/inference/xgboost_tests_requirements.txt 
b/sdks/python/apache_beam/ml/inference/xgboost_tests_requirements.txt
index e85f4131701..b1936b4149e 100644
--- a/sdks/python/apache_beam/ml/inference/xgboost_tests_requirements.txt
+++ b/sdks/python/apache_beam/ml/inference/xgboost_tests_requirements.txt
@@ -15,5 +15,5 @@
 # limitations under the License.
 #
 
-datatable==1.0.0
-xgboost>=1.6.0
\ No newline at end of file
+datatable>=1.1.0,<2
+xgboost>=2.0.0,<4
diff --git a/sdks/python/setup.py b/sdks/python/setup.py
index a983db5395e..3279282fc6b 100644
--- a/sdks/python/setup.py
+++ b/sdks/python/setup.py
@@ -166,6 +166,17 @@ dataframe_dependency = [
 
 milvus_dependency = ['pymilvus>=2.5.10,<3.0.0']
 qdrant_dependency = ['qdrant-client>=1.15.0']
+# NOTE: When updating these bounds, also update the bounds in
+# apache_beam/ml/inference/xgboost_tests_requirements.txt, which is used by the
+# XGBoost RunInference integration tests, and the py311-xgboost-* environments
+# in tox.ini.
+xgboost_dependency = [
+    'xgboost>=2.0.0,<4',
+    # apache_beam.ml.inference.xgboost_inference imports datatable directly.
+    # 1.1.0 is the first release with Python 3.10+ wheels; no release has
+    # wheels for Python 3.13+.
+    'datatable>=1.1.0,<2',
+]
 
 # google-adk / OpenTelemetry require protobuf>=5; tensorflow-transform in
 # ml_test is pinned to versions that require protobuf<5 on Python 3.10. Those
@@ -608,13 +619,12 @@ if __name__ == '__main__':
           'p310_ml_test': [
             'datatable',
           ] + ml_base + qdrant_dependency,
-          'p311_ml_test': [
-            'datatable',
-          ] + ml_base + qdrant_dependency,
+          # xgboost_dependency includes datatable, which only has wheels for
+          # Python <=3.12, so it is limited to the 3.11 and 3.12 test extras.
+          'p311_ml_test': ml_base + xgboost_dependency + qdrant_dependency,
           'p312_ml_test': [
-              'datatable',
               'typesafe-sdk>=0.7.1,<0.8',
-          ] + ml_base + qdrant_dependency,
+          ] + ml_base + xgboost_dependency + qdrant_dependency,
           # maintainer: milvus tests only run with this extension. Make sure it
           # is covered by docker-in-docker test when changing py version
           'p313_ml_test': ml_base_core + milvus_dependency + qdrant_dependency,
@@ -682,7 +692,7 @@ if __name__ == '__main__':
               'transformers==4.25.1',  # tensorflow transient dep
               'absl-py>=0.12.0'
           ],
-          'xgboost': ['xgboost>=1.6.0,<2.1.3', 'datatable==1.0.0'],
+          'xgboost': xgboost_dependency,
           'tensorflow-hub': ['tensorflow-hub>=0.14.0,<0.16.0'],
           'milvus': milvus_dependency,
           'qdrant': qdrant_dependency,
diff --git a/sdks/python/test-suites/tox/py311/build.gradle 
b/sdks/python/test-suites/tox/py311/build.gradle
index a2b0f0d5e23..433b0052b08 100644
--- a/sdks/python/test-suites/tox/py311/build.gradle
+++ b/sdks/python/test-suites/tox/py311/build.gradle
@@ -158,6 +158,15 @@ toxTask "testPy311transformers-latest", 
"py311-transformers-latest", "${posargs}
 test.dependsOn "testPy311transformers-latest"
 postCommitPyDep.dependsOn "testPy311transformers-latest"
 
+// Create a test task for each major version of xgboost
+toxTask "testPy311xgboost-200", "py311-xgboost-200", "${posargs}"
+test.dependsOn "testPy311xgboost-200"
+postCommitPyDep.dependsOn "testPy311xgboost-200"
+
+toxTask "testPy311xgboost-300", "py311-xgboost-300", "${posargs}"
+test.dependsOn "testPy311xgboost-300"
+postCommitPyDep.dependsOn "testPy311xgboost-300"
+
 toxTask "testPy311embeddingsMLTransform", "py311-embeddings", "${posargs}"
 test.dependsOn "testPy311embeddingsMLTransform"
 postCommitPyDep.dependsOn "testPy311embeddingsMLTransform"
diff --git a/sdks/python/tox.ini b/sdks/python/tox.ini
index c88e5e2fea6..a1bc7ba3322 100644
--- a/sdks/python/tox.ini
+++ b/sdks/python/tox.ini
@@ -489,14 +489,12 @@ commands =
   # Allow exit code 5 (no tests run) so that we can run this command safely on 
arbitrary subdirectories.
   /bin/sh -c 'pytest -o junit_suite_name={envname} 
--junitxml=pytest_{envname}.xml -n 6 -m uses_tf {posargs}; ret=$?; [ $ret = 5 ] 
&& exit 0 || exit $ret'
 
-[testenv:py311-xgboost-{160,170}]
+[testenv:py311-xgboost-{200,300}]
+# Keep these bounds in line with xgboost_dependency in setup.py.
 deps =
-  160:
-    xgboost>=1.6.0,<1.7.0
-    datatable==1.0.0
-  170:
-    xgboost>=1.7.0
-    datatable==1.0.0
+  200: xgboost>=2.0.0,<3.0.0
+  300: xgboost>=3.0.0,<4
+  datatable>=1.1.0,<2
 extras = test,gcp
 commands =
   # Log XGBoost version for debugging

Reply via email to