Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-s3transfer for 
openSUSE:Factory checked in at 2025-12-12 21:40:46
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-s3transfer (Old)
 and      /work/SRC/openSUSE:Factory/.python-s3transfer.new.1939 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-s3transfer"

Fri Dec 12 21:40:46 2025 rev:42 rq:1321832 version:0.16.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-s3transfer/python-s3transfer.changes      
2025-11-24 14:16:58.956993191 +0100
+++ 
/work/SRC/openSUSE:Factory/.python-s3transfer.new.1939/python-s3transfer.changes
    2025-12-12 21:41:18.429162103 +0100
@@ -1,0 +2,8 @@
+Tue Dec  9 12:31:58 UTC 2025 - John Paul Adrian Glaubitz 
<[email protected]>
+
+- Update to version 0.16.0
+  * feature:``awscrt``: ``CRTTransferManager`` now supports the following
+    ``TransferConfig`` options - ``multipart_threshold``, 
``multipart_chunksize``,
+    ``max_request_concurrency``
+
+-------------------------------------------------------------------

Old:
----
  s3transfer-0.15.0.tar.gz

New:
----
  s3transfer-0.16.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-s3transfer.spec ++++++
--- /var/tmp/diff_new_pack.HvFJs1/_old  2025-12-12 21:41:19.241196329 +0100
+++ /var/tmp/diff_new_pack.HvFJs1/_new  2025-12-12 21:41:19.241196329 +0100
@@ -18,7 +18,7 @@
 
 %{?sle15_python_module_pythons}
 Name:           python-s3transfer
-Version:        0.15.0
+Version:        0.16.0
 Release:        0
 Summary:        Python S3 transfer manager
 License:        Apache-2.0

++++++ s3transfer-0.15.0.tar.gz -> s3transfer-0.16.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/s3transfer-0.15.0/PKG-INFO 
new/s3transfer-0.16.0/PKG-INFO
--- old/s3transfer-0.15.0/PKG-INFO      2025-11-20 20:13:52.397613000 +0100
+++ new/s3transfer-0.16.0/PKG-INFO      2025-12-01 02:09:28.138971600 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: s3transfer
-Version: 0.15.0
+Version: 0.16.0
 Summary: An Amazon S3 Transfer Manager
 Home-page: https://github.com/boto/s3transfer
 Author: Amazon Web Services
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/s3transfer-0.15.0/s3transfer/__init__.py 
new/s3transfer-0.16.0/s3transfer/__init__.py
--- old/s3transfer-0.15.0/s3transfer/__init__.py        2025-11-20 
20:13:52.000000000 +0100
+++ new/s3transfer-0.16.0/s3transfer/__init__.py        2025-12-01 
02:09:27.000000000 +0100
@@ -146,7 +146,7 @@
 from s3transfer.exceptions import RetriesExceededError, S3UploadFailedError
 
 __author__ = 'Amazon Web Services'
-__version__ = '0.15.0'
+__version__ = '0.16.0'
 
 
 logger = logging.getLogger(__name__)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/s3transfer-0.15.0/s3transfer/crt.py 
new/s3transfer-0.16.0/s3transfer/crt.py
--- old/s3transfer-0.15.0/s3transfer/crt.py     2025-11-20 20:09:28.000000000 
+0100
+++ new/s3transfer-0.16.0/s3transfer/crt.py     2025-12-01 02:09:27.000000000 
+0100
@@ -13,6 +13,7 @@
 import logging
 import re
 import threading
+from collections import namedtuple
 from io import BytesIO
 
 import awscrt.http
@@ -184,6 +185,16 @@
     return target_gbps
 
 
+def _has_minimum_crt_version(minimum_version):
+    crt_version_str = awscrt.__version__
+    try:
+        crt_version_ints = map(int, crt_version_str.split("."))
+        crt_version_tuple = tuple(crt_version_ints)
+    except (TypeError, ValueError):
+        return False
+    return crt_version_tuple >= minimum_version
+
+
 class CRTTransferManager:
     ALLOWED_DOWNLOAD_ARGS = TransferManager.ALLOWED_DOWNLOAD_ARGS
     ALLOWED_UPLOAD_ARGS = TransferManager.ALLOWED_UPLOAD_ARGS
@@ -193,7 +204,9 @@
 
     _UNSUPPORTED_BUCKET_PATTERNS = TransferManager._UNSUPPORTED_BUCKET_PATTERNS
 
-    def __init__(self, crt_s3_client, crt_request_serializer, osutil=None):
+    def __init__(
+        self, crt_s3_client, crt_request_serializer, osutil=None, config=None
+    ):
         """A transfer manager interface for Amazon S3 on CRT s3 client.
 
         :type crt_s3_client: awscrt.s3.S3Client
@@ -207,12 +220,18 @@
         :type osutil: s3transfer.utils.OSUtils
         :param osutil: OSUtils object to use for os-related behavior when
             using with transfer manager.
+
+        :type config: s3transfer.manager.TransferConfig
+        :param config: The transfer configuration to be used when
+            making CRT S3 client requests.
         """
         if osutil is None:
             self._osutil = OSUtils()
         self._crt_s3_client = crt_s3_client
         self._s3_args_creator = S3ClientArgsCreator(
-            crt_request_serializer, self._osutil
+            crt_request_serializer,
+            self._osutil,
+            config,
         )
         self._crt_exception_translator = (
             crt_request_serializer.translate_crt_exception
@@ -731,10 +750,72 @@
         self._crt_future = self._s3_request.finished_future
 
 
+CRTConfigParameter = namedtuple('CRTConfigParameter', ['name', 'min_version'])
+
+
 class S3ClientArgsCreator:
-    def __init__(self, crt_request_serializer, os_utils):
+    _CRT_ARG_TO_CONFIG_PARAM = {
+        'max_active_connections_override': CRTConfigParameter(
+            'max_request_concurrency', (0, 29, 0)
+        ),
+    }
+
+    def __init__(self, crt_request_serializer, os_utils, config=None):
         self._request_serializer = crt_request_serializer
         self._os_utils = os_utils
+        self._config = config
+
+    def _get_crt_transfer_config_options(self, request_type):
+        crt_config = {
+            'part_size': self._config.multipart_chunksize,
+            'max_active_connections_override': 
self._config.max_request_concurrency,
+        }
+
+        if (
+            self._config.get_deep_attr('multipart_chunksize')
+            is self._config.UNSET_DEFAULT
+        ):
+            # Let CRT dynamically calculate part size.
+            crt_config['part_size'] = None
+        if (
+            self._config.get_deep_attr('max_request_concurrency')
+            is self._config.UNSET_DEFAULT
+        ):
+            crt_config['max_active_connections_override'] = None
+
+        if hasattr(self, f'_get_crt_options_{request_type}'):
+            crt_config.update(
+                getattr(self, f'_get_crt_options_{request_type}')()
+            )
+        self._remove_param_if_not_min_crt_version(crt_config)
+        return crt_config
+
+    def _get_crt_options_put_object(self):
+        return {'multipart_upload_threshold': self._config.multipart_threshold}
+
+    def _remove_param_if_not_min_crt_version(self, crt_config):
+        to_remove = []
+        for request_arg in crt_config:
+            if request_arg not in self._CRT_ARG_TO_CONFIG_PARAM:
+                continue
+            param = self._CRT_ARG_TO_CONFIG_PARAM[request_arg]
+            if _has_minimum_crt_version(param.min_version):
+                continue
+            # Only log the warning if user attempted to explicitly
+            # use the transfer config parameter.
+            if (
+                self._config.get_deep_attr(param.name)
+                is not self._config.UNSET_DEFAULT
+            ):
+                min_ver_str = '.'.join(str(i) for i in param.min_version)
+                logger.warning(
+                    f'Transfer config parameter {param.name} '
+                    f'requires minimum CRT version: {min_ver_str}. '
+                    f'{param.name} will not be used in the request.'
+                )
+            to_remove.append(request_arg)
+        for request_arg in to_remove:
+            del crt_config[request_arg]
 
     def get_make_request_args(
         self, request_type, call_args, coordinator, future, on_done_after_calls
@@ -823,6 +904,10 @@
         )
         make_request_args['send_filepath'] = send_filepath
         make_request_args['checksum_config'] = checksum_config
+        if self._config is not None:
+            make_request_args.update(
+                self._get_crt_transfer_config_options(request_type)
+            )
         return make_request_args
 
     def _get_make_request_args_get_object(
@@ -859,6 +944,10 @@
         make_request_args['recv_filepath'] = recv_filepath
         make_request_args['on_body'] = on_body
         make_request_args['checksum_config'] = checksum_config
+        if self._config is not None:
+            make_request_args.update(
+                self._get_crt_transfer_config_options(request_type)
+            )
         return make_request_args
 
     def _default_get_make_request_args(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/s3transfer-0.15.0/s3transfer/manager.py 
new/s3transfer-0.16.0/s3transfer/manager.py
--- old/s3transfer-0.15.0/s3transfer/manager.py 2025-11-20 20:09:28.000000000 
+0100
+++ new/s3transfer-0.16.0/s3transfer/manager.py 2025-12-01 02:09:27.000000000 
+0100
@@ -50,6 +50,8 @@
 
 
 class TransferConfig:
+    UNSET_DEFAULT = object()
+
     def __init__(
         self,
         multipart_threshold=8 * MB,
@@ -152,12 +154,19 @@
 
     def _validate_attrs_are_nonzero(self):
         for attr, attr_val in self.__dict__.items():
-            if attr_val is not None and attr_val <= 0:
+            if (
+                attr_val is not None
+                and attr_val is not self.UNSET_DEFAULT
+                and attr_val <= 0
+            ):
                 raise ValueError(
                     f'Provided parameter {attr} of value {attr_val} must '
                     'be greater than 0.'
                 )
 
+    def get_deep_attr(self, item):
+        return object.__getattribute__(self, item)
+
 
 class TransferManager:
     ALLOWED_DOWNLOAD_ARGS = ALLOWED_DOWNLOAD_ARGS
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/s3transfer-0.15.0/s3transfer.egg-info/PKG-INFO 
new/s3transfer-0.16.0/s3transfer.egg-info/PKG-INFO
--- old/s3transfer-0.15.0/s3transfer.egg-info/PKG-INFO  2025-11-20 
20:13:52.000000000 +0100
+++ new/s3transfer-0.16.0/s3transfer.egg-info/PKG-INFO  2025-12-01 
02:09:28.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: s3transfer
-Version: 0.15.0
+Version: 0.16.0
 Summary: An Amazon S3 Transfer Manager
 Home-page: https://github.com/boto/s3transfer
 Author: Amazon Web Services
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/s3transfer-0.15.0/tests/functional/test_crt.py 
new/s3transfer-0.16.0/tests/functional/test_crt.py
--- old/s3transfer-0.15.0/tests/functional/test_crt.py  2025-11-20 
20:09:28.000000000 +0100
+++ new/s3transfer-0.16.0/tests/functional/test_crt.py  2025-12-01 
02:09:27.000000000 +0100
@@ -18,6 +18,8 @@
 
 from botocore.session import Session
 
+from s3transfer.constants import MB
+from s3transfer.manager import TransferConfig
 from s3transfer.subscribers import BaseSubscriber
 from tests import (
     HAS_CRT,
@@ -769,3 +771,98 @@
         )
         with self.assertRaises(awscrt.exceptions.AwsCrtError):
             future.result()
+
+    def test_transfer_config_used_in_upload_request(self):
+        config = TransferConfig(
+            multipart_threshold=4 * MB,
+            multipart_chunksize=2 * MB,
+            max_request_concurrency=100,
+        )
+        transfer_manager = s3transfer.crt.CRTTransferManager(
+            crt_s3_client=self.s3_crt_client,
+            crt_request_serializer=self.request_serializer,
+            config=config,
+        )
+        future = transfer_manager.upload(
+            self.filename, self.bucket, self.key, {}, []
+        )
+        future.result()
+
+        callargs_kwargs = self.s3_crt_client.make_request.call_args[1]
+        assert callargs_kwargs['multipart_upload_threshold'] == 4 * MB
+        assert callargs_kwargs['part_size'] == 2 * MB
+        assert callargs_kwargs['max_active_connections_override'] == 100
+
+    def test_transfer_config_used_in_download_request(self):
+        config = TransferConfig(
+            multipart_threshold=4 * MB,
+            multipart_chunksize=2 * MB,
+            max_request_concurrency=100,
+        )
+        transfer_manager = s3transfer.crt.CRTTransferManager(
+            crt_s3_client=self.s3_crt_client,
+            crt_request_serializer=self.request_serializer,
+            config=config,
+        )
+        future = transfer_manager.download(
+            self.bucket, self.key, self.filename, {}, []
+        )
+        future.result()
+
+        callargs_kwargs = self.s3_crt_client.make_request.call_args[1]
+        assert callargs_kwargs['part_size'] == 2 * MB
+        assert callargs_kwargs['max_active_connections_override'] == 100
+        # Config option only used for PUT requests.
+        assert 'multipart_upload_threshold' not in callargs_kwargs
+
+    def test_unset_part_size_defaults_to_none_in_upload_request(self):
+        config = TransferConfig(
+            multipart_chunksize=TransferConfig.UNSET_DEFAULT,
+        )
+        transfer_manager = s3transfer.crt.CRTTransferManager(
+            crt_s3_client=self.s3_crt_client,
+            crt_request_serializer=self.request_serializer,
+            config=config,
+        )
+        future = transfer_manager.upload(
+            self.filename, self.bucket, self.key, {}, []
+        )
+        future.result()
+
+        callargs_kwargs = self.s3_crt_client.make_request.call_args[1]
+        assert callargs_kwargs['part_size'] is None
+
+    def test_unset_max_concurrency_defaults_to_none(self):
+        config = TransferConfig(
+            max_request_concurrency=TransferConfig.UNSET_DEFAULT,
+        )
+        transfer_manager = s3transfer.crt.CRTTransferManager(
+            crt_s3_client=self.s3_crt_client,
+            crt_request_serializer=self.request_serializer,
+            config=config,
+        )
+        future = transfer_manager.upload(
+            self.filename, self.bucket, self.key, {}, []
+        )
+        future.result()
+
+        callargs_kwargs = self.s3_crt_client.make_request.call_args[1]
+        assert callargs_kwargs['max_active_connections_override'] is None
+
+    @mock.patch('awscrt.__version__', '0.28.0')
+    def test_args_removed_if_not_min_awscrt_version(self):
+        config = TransferConfig(
+            max_request_concurrency=TransferConfig.UNSET_DEFAULT,
+        )
+        transfer_manager = s3transfer.crt.CRTTransferManager(
+            crt_s3_client=self.s3_crt_client,
+            crt_request_serializer=self.request_serializer,
+            config=config,
+        )
+        future = transfer_manager.upload(
+            self.filename, self.bucket, self.key, {}, []
+        )
+        future.result()
+
+        callargs_kwargs = self.s3_crt_client.make_request.call_args[1]
+        assert 'max_active_connections_override' not in callargs_kwargs

Reply via email to