)]}'
{"/PATCHSET_LEVEL":[{"author":{"_account_id":39146,"name":"Nathaniel Martes","display_name":"Nate Martes","email":"nmartes@NVIDIA.com","username":"nmartes"},"change_message_id":"623af8a227f0333d100b1de1bfd190b6a98e6cc4","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":2,"id":"c18d1cbc_d496087f","updated":"2026-08-25 22:35:31.000000000","message":"This was pulled out of 993193: s3api: Checksum Persistence on MPUs | https://review.opendev.org/c/openstack/swift/+/993193 for ease of reviewing","commit_id":"14e090fb9ec571aea763a87cfdf5800fee7fe8b1"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2529f712f5bd134ad4902bcd8fe2830037f0f9b2","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":6,"id":"246a85a4_11d25ffd","updated":"2026-08-26 16:52:23.000000000","message":"I think my most significant feedback on this patch is can we keep things simple by just always persisting checksum sysmeta *whenever* it is calculated, and just filter response headers when they are not expected to have the checksum headers? My goal being less special cases, and consistent expectations of what we find on disk in object metadata.","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"},{"author":{"_account_id":39146,"name":"Nathaniel Martes","display_name":"Nate Martes","email":"nmartes@NVIDIA.com","username":"nmartes"},"change_message_id":"33ccd9b9646af2bd54a5ffcd9d579938f1154590","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":6,"id":"a521c6ef_2241a77d","in_reply_to":"246a85a4_11d25ffd","updated":"2026-08-27 05:29:48.000000000","message":"\u003e  and just filter response headers when they are not expected to have the checksum headers? My goal being less special cases, and consistent expectations of what we find on disk in object metadata.\n\nYea I think this is more straightfoward, though we don\u0027t need to store that data, it is much eaiser to develop under if every checksummed object that has their sysmeta persisted, is all the same sysmeta","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"},{"author":{"_account_id":39146,"name":"Nathaniel Martes","display_name":"Nate Martes","email":"nmartes@NVIDIA.com","username":"nmartes"},"change_message_id":"cf8a5361a2ad7205166922072366e4165098f391","unresolved":true,"context_lines":[],"source_content_type":"","patch_set":10,"id":"f9b656fe_55e3e0a1","updated":"2026-08-27 22:24:20.000000000","message":"Just so we have it somewhere:\n\nfuture us will have to think of the following situations:\n\nListParts on Checksum MPUs when the client did not make the MPU checksum-aware\n\nAWS does not expose the part checksums in this case, so we need to make sure not to even though every part will have checksum metadata\nGetObject \u0026 HeadObject on Checksum MPUs when the client did not make the MPU checksum-aware\n\nAWS (for COMPOSITE checksum MPUs) exposes part checksum when a client GETs the object part. We should not expose the part checksum even if it exists unless it is a COMPOSITE MPU checksum","commit_id":"3c24b3be1b69799071071840ad7fb92e670e8b0c"},{"author":{"_account_id":39146,"name":"Nathaniel Martes","display_name":"Nate Martes","email":"nmartes@NVIDIA.com","username":"nmartes"},"change_message_id":"b61e9fb0f50f45febde6f178293d76ed19cf3857","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":10,"id":"be678a2d_de005ba8","updated":"2026-08-27 22:06:56.000000000","message":"Some things here:\n\nSquashed in 1002659: sq? always persist mpu part checksum sysmeta when available | https://review.opendev.org/c/openstack/swift/+/1002659\n\nI had done refactoring of some test cases, however, I think to get this over the line, we can do that later so I pushed up a patch near the end of the chain for that 1002697: tests: clean up s3api/test_object_checksums | https://review.opendev.org/c/openstack/swift/+/1002697","commit_id":"3c24b3be1b69799071071840ad7fb92e670e8b0c"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"558e28f4468f6b202ca4458528be06d5c48434a4","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":14,"id":"46d1fad2_41c070cc","updated":"2026-08-28 12:10:16.000000000","message":"I\u0027ve run out of time for full review but leaving a +1 to indicate that I\u0027m reasonably confident we\u0027ve got this patch in good shape","commit_id":"c4841758aa7979be753960c6f22dfde9e1d02c4e"}],"swift/common/middleware/s3api/controllers/multi_upload.py":[{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2529f712f5bd134ad4902bcd8fe2830037f0f9b2","unresolved":true,"context_lines":[{"line_number":105,"context_line":"MAX_COMPLETE_UPLOAD_BODY_SIZE \u003d 2048 * 1024"},{"line_number":106,"context_line":""},{"line_number":107,"context_line":""},{"line_number":108,"context_line":"def get_checksum_hasher(header):"},{"line_number":109,"context_line":"    \"\"\"Return an S3 checksum hasher or raise an S3 response error.\"\"\""},{"line_number":110,"context_line":"    # This is to avoid cyclical imports between checksum and s3response."},{"line_number":111,"context_line":"    try:"}],"source_content_type":"text/x-python","patch_set":6,"id":"db056785_db2dd75a","line":108,"updated":"2026-08-26 16:52:23.000000000","message":"in s3request this is underscore prefixed ``_get_checksum_hasher`` so that the import does not need to be aliased. I think it would be better to be consistent and do the same here.\n\nalthough Claude disagrees with me!","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"},{"author":{"_account_id":39146,"name":"Nathaniel Martes","display_name":"Nate Martes","email":"nmartes@NVIDIA.com","username":"nmartes"},"change_message_id":"be3596e58080e312b104b801368b340a2e862ea0","unresolved":false,"context_lines":[{"line_number":105,"context_line":"MAX_COMPLETE_UPLOAD_BODY_SIZE \u003d 2048 * 1024"},{"line_number":106,"context_line":""},{"line_number":107,"context_line":""},{"line_number":108,"context_line":"def get_checksum_hasher(header):"},{"line_number":109,"context_line":"    \"\"\"Return an S3 checksum hasher or raise an S3 response error.\"\"\""},{"line_number":110,"context_line":"    # This is to avoid cyclical imports between checksum and s3response."},{"line_number":111,"context_line":"    try:"}],"source_content_type":"text/x-python","patch_set":6,"id":"7175c72a_cd2939cb","line":108,"in_reply_to":"7971e6fc_03ac7d11","updated":"2026-08-27 06:36:59.000000000","message":"Done","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"},{"author":{"_account_id":39146,"name":"Nathaniel Martes","display_name":"Nate Martes","email":"nmartes@NVIDIA.com","username":"nmartes"},"change_message_id":"33ccd9b9646af2bd54a5ffcd9d579938f1154590","unresolved":true,"context_lines":[{"line_number":105,"context_line":"MAX_COMPLETE_UPLOAD_BODY_SIZE \u003d 2048 * 1024"},{"line_number":106,"context_line":""},{"line_number":107,"context_line":""},{"line_number":108,"context_line":"def get_checksum_hasher(header):"},{"line_number":109,"context_line":"    \"\"\"Return an S3 checksum hasher or raise an S3 response error.\"\"\""},{"line_number":110,"context_line":"    # This is to avoid cyclical imports between checksum and s3response."},{"line_number":111,"context_line":"    try:"}],"source_content_type":"text/x-python","patch_set":6,"id":"7971e6fc_03ac7d11","line":108,"in_reply_to":"db056785_db2dd75a","updated":"2026-08-27 05:29:48.000000000","message":"I also disagree with claude here. We should be consist with what we do across files.","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2529f712f5bd134ad4902bcd8fe2830037f0f9b2","unresolved":true,"context_lines":[{"line_number":278,"context_line":"    # which will be COMPOSITE, unless the algorithm cannot"},{"line_number":279,"context_line":"    # be COMPOSITE"},{"line_number":280,"context_line":"    if checksum_attrs.type is None:"},{"line_number":281,"context_line":"        if (checksum_attrs.header not in"},{"line_number":282,"context_line":"                COMPOSITE_MPU_CHECKSUM_HEADERS):"},{"line_number":283,"context_line":"            checksum_attrs.type \u003d CHECKSUM_TYPE_FULL_OBJECT"},{"line_number":284,"context_line":"        else:"}],"source_content_type":"text/x-python","patch_set":6,"id":"fc41b640_31899bdf","line":281,"updated":"2026-08-26 16:52:23.000000000","message":"nit: the ``not`` can be avoided by inverting the logic of the if/else","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"},{"author":{"_account_id":39146,"name":"Nathaniel Martes","display_name":"Nate Martes","email":"nmartes@NVIDIA.com","username":"nmartes"},"change_message_id":"be3596e58080e312b104b801368b340a2e862ea0","unresolved":false,"context_lines":[{"line_number":278,"context_line":"    # which will be COMPOSITE, unless the algorithm cannot"},{"line_number":279,"context_line":"    # be COMPOSITE"},{"line_number":280,"context_line":"    if checksum_attrs.type is None:"},{"line_number":281,"context_line":"        if (checksum_attrs.header not in"},{"line_number":282,"context_line":"                COMPOSITE_MPU_CHECKSUM_HEADERS):"},{"line_number":283,"context_line":"            checksum_attrs.type \u003d CHECKSUM_TYPE_FULL_OBJECT"},{"line_number":284,"context_line":"        else:"}],"source_content_type":"text/x-python","patch_set":6,"id":"40d434de_e726ad0e","line":281,"in_reply_to":"5f17bbfb_53096eff","updated":"2026-08-27 06:36:59.000000000","message":"Done","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"},{"author":{"_account_id":39146,"name":"Nathaniel Martes","display_name":"Nate Martes","email":"nmartes@NVIDIA.com","username":"nmartes"},"change_message_id":"33ccd9b9646af2bd54a5ffcd9d579938f1154590","unresolved":true,"context_lines":[{"line_number":278,"context_line":"    # which will be COMPOSITE, unless the algorithm cannot"},{"line_number":279,"context_line":"    # be COMPOSITE"},{"line_number":280,"context_line":"    if checksum_attrs.type is None:"},{"line_number":281,"context_line":"        if (checksum_attrs.header not in"},{"line_number":282,"context_line":"                COMPOSITE_MPU_CHECKSUM_HEADERS):"},{"line_number":283,"context_line":"            checksum_attrs.type \u003d CHECKSUM_TYPE_FULL_OBJECT"},{"line_number":284,"context_line":"        else:"}],"source_content_type":"text/x-python","patch_set":6,"id":"5f17bbfb_53096eff","line":281,"in_reply_to":"fc41b640_31899bdf","updated":"2026-08-27 05:29:48.000000000","message":"Yea this might look cleaner","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2529f712f5bd134ad4902bcd8fe2830037f0f9b2","unresolved":true,"context_lines":[{"line_number":290,"context_line":"    checksum_hasher \u003d get_checksum_hasher(checksum_attrs.header)"},{"line_number":291,"context_line":"    if checksum_attrs.is_full_object:"},{"line_number":292,"context_line":"        # The current proxy server might not have a crc_combine function"},{"line_number":293,"context_line":"        # so lets not make a MPU we cannot complete"},{"line_number":294,"context_line":"        if checksum_hasher.crc_combine_func is None:"},{"line_number":295,"context_line":"            raise S3NotImplemented("},{"line_number":296,"context_line":"                \u0027The %s algorithm is not supported for full object \u0027"}],"source_content_type":"text/x-python","patch_set":6,"id":"9f6c441b_58c08d5c","line":293,"updated":"2026-08-26 16:52:23.000000000","message":"...although what matters is if the proxy handling the *mpu complete* has anycrc, which leads to an interesting upgrade story.\n\nBut IMHO we should make this early check, here, because if this proxy does not have anycrc then there\u0027s a chance the completing proxy won\u0027t; and also note: we must make the same check during complete.","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"},{"author":{"_account_id":39146,"name":"Nathaniel Martes","display_name":"Nate Martes","email":"nmartes@NVIDIA.com","username":"nmartes"},"change_message_id":"33ccd9b9646af2bd54a5ffcd9d579938f1154590","unresolved":true,"context_lines":[{"line_number":290,"context_line":"    checksum_hasher \u003d get_checksum_hasher(checksum_attrs.header)"},{"line_number":291,"context_line":"    if checksum_attrs.is_full_object:"},{"line_number":292,"context_line":"        # The current proxy server might not have a crc_combine function"},{"line_number":293,"context_line":"        # so lets not make a MPU we cannot complete"},{"line_number":294,"context_line":"        if checksum_hasher.crc_combine_func is None:"},{"line_number":295,"context_line":"            raise S3NotImplemented("},{"line_number":296,"context_line":"                \u0027The %s algorithm is not supported for full object \u0027"}],"source_content_type":"text/x-python","patch_set":6,"id":"a538e330_30d073f7","line":293,"in_reply_to":"9f6c441b_58c08d5c","updated":"2026-08-27 05:29:48.000000000","message":"I agree, I like that the check runs early and we should do the same with CompleteMultiPartUpload","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2bd7377e84358203b53ac6c16ba3466f636da3de","unresolved":false,"context_lines":[{"line_number":290,"context_line":"    checksum_hasher \u003d get_checksum_hasher(checksum_attrs.header)"},{"line_number":291,"context_line":"    if checksum_attrs.is_full_object:"},{"line_number":292,"context_line":"        # The current proxy server might not have a crc_combine function"},{"line_number":293,"context_line":"        # so lets not make a MPU we cannot complete"},{"line_number":294,"context_line":"        if checksum_hasher.crc_combine_func is None:"},{"line_number":295,"context_line":"            raise S3NotImplemented("},{"line_number":296,"context_line":"                \u0027The %s algorithm is not supported for full object \u0027"}],"source_content_type":"text/x-python","patch_set":6,"id":"b9add0a4_dd57e738","line":293,"in_reply_to":"a538e330_30d073f7","updated":"2026-08-27 15:16:04.000000000","message":"Done","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2529f712f5bd134ad4902bcd8fe2830037f0f9b2","unresolved":true,"context_lines":[{"line_number":323,"context_line":"        # the wrapper ourselves"},{"line_number":324,"context_line":"        req.install_checksumming_input("},{"line_number":325,"context_line":"            checksum_hasher, checksum_attrs.header, None)"},{"line_number":326,"context_line":"    else:"},{"line_number":327,"context_line":"        raise InvalidRequest("},{"line_number":328,"context_line":"            \u0027Checksum Type mismatch occurred, expected checksum \u0027"},{"line_number":329,"context_line":"            \u0027Type: %s, actual checksum Type: null\u0027 %"}],"source_content_type":"text/x-python","patch_set":6,"id":"4dd14b7f_5da74543","line":326,"updated":"2026-08-26 16:52:23.000000000","message":"I keep tripping up on this; could we add a comment that S3 uses \u0027type\u0027 to refer to algorithm","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"},{"author":{"_account_id":39146,"name":"Nathaniel Martes","display_name":"Nate Martes","email":"nmartes@NVIDIA.com","username":"nmartes"},"change_message_id":"be3596e58080e312b104b801368b340a2e862ea0","unresolved":false,"context_lines":[{"line_number":323,"context_line":"        # the wrapper ourselves"},{"line_number":324,"context_line":"        req.install_checksumming_input("},{"line_number":325,"context_line":"            checksum_hasher, checksum_attrs.header, None)"},{"line_number":326,"context_line":"    else:"},{"line_number":327,"context_line":"        raise InvalidRequest("},{"line_number":328,"context_line":"            \u0027Checksum Type mismatch occurred, expected checksum \u0027"},{"line_number":329,"context_line":"            \u0027Type: %s, actual checksum Type: null\u0027 %"}],"source_content_type":"text/x-python","patch_set":6,"id":"7f5cbb64_94eaf259","line":326,"in_reply_to":"0c3e7337_8d4c464b","updated":"2026-08-27 06:36:59.000000000","message":"Done","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"},{"author":{"_account_id":39146,"name":"Nathaniel Martes","display_name":"Nate Martes","email":"nmartes@NVIDIA.com","username":"nmartes"},"change_message_id":"33ccd9b9646af2bd54a5ffcd9d579938f1154590","unresolved":true,"context_lines":[{"line_number":323,"context_line":"        # the wrapper ourselves"},{"line_number":324,"context_line":"        req.install_checksumming_input("},{"line_number":325,"context_line":"            checksum_hasher, checksum_attrs.header, None)"},{"line_number":326,"context_line":"    else:"},{"line_number":327,"context_line":"        raise InvalidRequest("},{"line_number":328,"context_line":"            \u0027Checksum Type mismatch occurred, expected checksum \u0027"},{"line_number":329,"context_line":"            \u0027Type: %s, actual checksum Type: null\u0027 %"}],"source_content_type":"text/x-python","patch_set":6,"id":"0c3e7337_8d4c464b","line":326,"in_reply_to":"4dd14b7f_5da74543","updated":"2026-08-27 05:29:48.000000000","message":"Sure!","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2529f712f5bd134ad4902bcd8fe2830037f0f9b2","unresolved":true,"context_lines":[{"line_number":446,"context_line":"            _validate_upload_part_checksum_headers("},{"line_number":447,"context_line":"                req, mpu_checksum_attrs)"},{"line_number":448,"context_line":"            # Parts persist the checksum header and value without an"},{"line_number":449,"context_line":"            # object-level checksum type."},{"line_number":450,"context_line":"            req.enable_checksum_persistence(None)"},{"line_number":451,"context_line":""},{"line_number":452,"context_line":"        req.container_name +\u003d MULTIUPLOAD_SUFFIX"}],"source_content_type":"text/x-python","patch_set":6,"id":"786375e5_6fca4a32","line":449,"updated":"2026-08-26 16:52:23.000000000","message":"I feel like *Swift\u0027s* internal implementation could be simpler: the type of an object PUT checksum is always FULL_OBJECT, we just don\u0027t return that header with an UploadPartCopy response.\n\nThen we can drop the arg to ``enable_checksum_persistence``, always expect the same set of sysmeta, and just vary the response headers.","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"},{"author":{"_account_id":39146,"name":"Nathaniel Martes","display_name":"Nate Martes","email":"nmartes@NVIDIA.com","username":"nmartes"},"change_message_id":"be3596e58080e312b104b801368b340a2e862ea0","unresolved":false,"context_lines":[{"line_number":446,"context_line":"            _validate_upload_part_checksum_headers("},{"line_number":447,"context_line":"                req, mpu_checksum_attrs)"},{"line_number":448,"context_line":"            # Parts persist the checksum header and value without an"},{"line_number":449,"context_line":"            # object-level checksum type."},{"line_number":450,"context_line":"            req.enable_checksum_persistence(None)"},{"line_number":451,"context_line":""},{"line_number":452,"context_line":"        req.container_name +\u003d MULTIUPLOAD_SUFFIX"}],"source_content_type":"text/x-python","patch_set":6,"id":"665c7df1_3b9656ef","line":449,"in_reply_to":"569507ed_c2c6632b","updated":"2026-08-27 06:36:59.000000000","message":"Done","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"},{"author":{"_account_id":39146,"name":"Nathaniel Martes","display_name":"Nate Martes","email":"nmartes@NVIDIA.com","username":"nmartes"},"change_message_id":"33ccd9b9646af2bd54a5ffcd9d579938f1154590","unresolved":true,"context_lines":[{"line_number":446,"context_line":"            _validate_upload_part_checksum_headers("},{"line_number":447,"context_line":"                req, mpu_checksum_attrs)"},{"line_number":448,"context_line":"            # Parts persist the checksum header and value without an"},{"line_number":449,"context_line":"            # object-level checksum type."},{"line_number":450,"context_line":"            req.enable_checksum_persistence(None)"},{"line_number":451,"context_line":""},{"line_number":452,"context_line":"        req.container_name +\u003d MULTIUPLOAD_SUFFIX"}],"source_content_type":"text/x-python","patch_set":6,"id":"569507ed_c2c6632b","line":449,"in_reply_to":"786375e5_6fca4a32","updated":"2026-08-27 05:29:48.000000000","message":"Yea I think this is more straightfoward, though we don\u0027t need to store that data, it is much eaiser to develop under if every checksummed object that has their sysmeta persisted, is all the same sysmeta","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2529f712f5bd134ad4902bcd8fe2830037f0f9b2","unresolved":true,"context_lines":[{"line_number":489,"context_line":""},{"line_number":490,"context_line":"        if req.checksum_sysmeta_footers:"},{"line_number":491,"context_line":"            # UploadPart requests do not echo the type,"},{"line_number":492,"context_line":"            # so do not require it here"},{"line_number":493,"context_line":"            resp.headers.update(checksum_sysmeta_to_response_headers("},{"line_number":494,"context_line":"                req.checksum_sysmeta_footers, require_type\u003dFalse))"},{"line_number":495,"context_line":""}],"source_content_type":"text/x-python","patch_set":6,"id":"e38d1739_95079ffd","line":492,"updated":"2026-08-26 16:52:23.000000000","message":"the meaning of the comment is a bit ambiguous without understanding that we don\u0027t persist type for an UploadPart. Can we just keep it simple and always persist ``(type, algorithm, value)`` for any put and then filter out ``type`` in ``UploadPartCopy`` responses?\n\nfewer variations -\u003e less code, less tests -\u003e easier to maintain","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"},{"author":{"_account_id":39146,"name":"Nathaniel Martes","display_name":"Nate Martes","email":"nmartes@NVIDIA.com","username":"nmartes"},"change_message_id":"be3596e58080e312b104b801368b340a2e862ea0","unresolved":false,"context_lines":[{"line_number":489,"context_line":""},{"line_number":490,"context_line":"        if req.checksum_sysmeta_footers:"},{"line_number":491,"context_line":"            # UploadPart requests do not echo the type,"},{"line_number":492,"context_line":"            # so do not require it here"},{"line_number":493,"context_line":"            resp.headers.update(checksum_sysmeta_to_response_headers("},{"line_number":494,"context_line":"                req.checksum_sysmeta_footers, require_type\u003dFalse))"},{"line_number":495,"context_line":""}],"source_content_type":"text/x-python","patch_set":6,"id":"dbef6bd7_00dd42d0","line":492,"in_reply_to":"bb7d3866_f45c3f93","updated":"2026-08-27 06:36:59.000000000","message":"Done","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"},{"author":{"_account_id":39146,"name":"Nathaniel Martes","display_name":"Nate Martes","email":"nmartes@NVIDIA.com","username":"nmartes"},"change_message_id":"33ccd9b9646af2bd54a5ffcd9d579938f1154590","unresolved":true,"context_lines":[{"line_number":489,"context_line":""},{"line_number":490,"context_line":"        if req.checksum_sysmeta_footers:"},{"line_number":491,"context_line":"            # UploadPart requests do not echo the type,"},{"line_number":492,"context_line":"            # so do not require it here"},{"line_number":493,"context_line":"            resp.headers.update(checksum_sysmeta_to_response_headers("},{"line_number":494,"context_line":"                req.checksum_sysmeta_footers, require_type\u003dFalse))"},{"line_number":495,"context_line":""}],"source_content_type":"text/x-python","patch_set":6,"id":"bb7d3866_f45c3f93","line":492,"in_reply_to":"e38d1739_95079ffd","updated":"2026-08-27 05:29:48.000000000","message":"Sure I think it will be easier to maintain too","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"}],"swift/common/middleware/s3api/s3api.py":[{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2529f712f5bd134ad4902bcd8fe2830037f0f9b2","unresolved":true,"context_lines":[{"line_number":387,"context_line":"            elif hdr_key in (\u0027x-amz-checksum-algorithm\u0027,"},{"line_number":388,"context_line":"                             \u0027x-amz-sdk-checksum-algorithm\u0027):"},{"line_number":389,"context_line":"                hdr_val_normalised \u003d normalize_checksum_algorithm("},{"line_number":390,"context_line":"                    hdr_val).replace(\u0027-\u0027, \u0027\u0027)"},{"line_number":391,"context_line":"                if hdr_val_normalised in WELL_KNOWN_CHECKSUM_ALGORITHMS:"},{"line_number":392,"context_line":"                    label_val \u003d hdr_val_normalised"},{"line_number":393,"context_line":"                else:"}],"source_content_type":"text/x-python","patch_set":6,"id":"eeaa3738_e0649ffb","line":390,"range":{"start_line":390,"start_character":29,"end_line":390,"end_character":45},"updated":"2026-08-26 16:52:23.000000000","message":"let\u0027s do this in the normalize function, and put the commentary above in there too so we have it all in one place.\n\nalso, here\u0027s an opportunity for another small patch up front of chain we could merge \"s3api: introduce normalize_checksum_algorithm helper\", especially if it eliminates s3api.py diff hunks from these patches","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"},{"author":{"_account_id":39146,"name":"Nathaniel Martes","display_name":"Nate Martes","email":"nmartes@NVIDIA.com","username":"nmartes"},"change_message_id":"be3596e58080e312b104b801368b340a2e862ea0","unresolved":false,"context_lines":[{"line_number":387,"context_line":"            elif hdr_key in (\u0027x-amz-checksum-algorithm\u0027,"},{"line_number":388,"context_line":"                             \u0027x-amz-sdk-checksum-algorithm\u0027):"},{"line_number":389,"context_line":"                hdr_val_normalised \u003d normalize_checksum_algorithm("},{"line_number":390,"context_line":"                    hdr_val).replace(\u0027-\u0027, \u0027\u0027)"},{"line_number":391,"context_line":"                if hdr_val_normalised in WELL_KNOWN_CHECKSUM_ALGORITHMS:"},{"line_number":392,"context_line":"                    label_val \u003d hdr_val_normalised"},{"line_number":393,"context_line":"                else:"}],"source_content_type":"text/x-python","patch_set":6,"id":"1b04b6e6_5d54831b","line":390,"range":{"start_line":390,"start_character":29,"end_line":390,"end_character":45},"in_reply_to":"48492cda_79a3bbd8","updated":"2026-08-27 06:36:59.000000000","message":"See 1002583: s3api: add normalize_checksum_algorithm function | https://review.opendev.org/c/openstack/swift/+/1002583","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"},{"author":{"_account_id":39146,"name":"Nathaniel Martes","display_name":"Nate Martes","email":"nmartes@NVIDIA.com","username":"nmartes"},"change_message_id":"33ccd9b9646af2bd54a5ffcd9d579938f1154590","unresolved":true,"context_lines":[{"line_number":387,"context_line":"            elif hdr_key in (\u0027x-amz-checksum-algorithm\u0027,"},{"line_number":388,"context_line":"                             \u0027x-amz-sdk-checksum-algorithm\u0027):"},{"line_number":389,"context_line":"                hdr_val_normalised \u003d normalize_checksum_algorithm("},{"line_number":390,"context_line":"                    hdr_val).replace(\u0027-\u0027, \u0027\u0027)"},{"line_number":391,"context_line":"                if hdr_val_normalised in WELL_KNOWN_CHECKSUM_ALGORITHMS:"},{"line_number":392,"context_line":"                    label_val \u003d hdr_val_normalised"},{"line_number":393,"context_line":"                else:"}],"source_content_type":"text/x-python","patch_set":6,"id":"48492cda_79a3bbd8","line":390,"range":{"start_line":390,"start_character":29,"end_line":390,"end_character":45},"in_reply_to":"eeaa3738_e0649ffb","updated":"2026-08-27 05:29:48.000000000","message":"So we cannot actually put `replace(\u0027-\u0027, \u0027\u0027)` inside of normalize because AWS does not accept checksum algorithm names with hypens in them, so if we normalize them out, we would be fixing what should actually be an error.\n\n```\naws s3api \\\ncreate-multipart-upload \\\n--bucket bar --key foo \\\n--checksum-algorithm sha-256\n\naws: [ERROR]: An error occurred (InvalidRequest)\nwhen calling the CreateMultipartUpload operation: Checksum algorithm \nprovided is unsupported. Please try again with \nany of the valid types: [CRC32, CRC32C, CRC64NVME, MD5, SHA1, SHA256, \nSHA512, XXHASH128, XXHASH3, XXHASH64]\n```\n\nBut I think to move this as a small intro patch is good!","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"}],"swift/common/middleware/s3api/s3checksum.py":[{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2529f712f5bd134ad4902bcd8fe2830037f0f9b2","unresolved":true,"context_lines":[{"line_number":85,"context_line":"def checksum_sysmeta_headers(checksum_algorithm, checksum_value,"},{"line_number":86,"context_line":"                             checksum_type):"},{"line_number":87,"context_line":"    \"\"\"Return object sysmeta for a calculated or validated checksum.\"\"\""},{"line_number":88,"context_line":"    return {"},{"line_number":89,"context_line":"        s3api_sysmeta_header(\u0027object\u0027, \u0027checksum-algorithm\u0027):"},{"line_number":90,"context_line":"            normalize_checksum_algorithm(checksum_algorithm),"},{"line_number":91,"context_line":"        s3api_sysmeta_header(\u0027object\u0027, \u0027checksum-value\u0027): checksum_value,"}],"source_content_type":"text/x-python","patch_set":6,"id":"62ef3533_55c1c007","side":"PARENT","line":88,"updated":"2026-08-26 16:52:23.000000000","message":"```\nreturn HeaderKeyDict({...})\n```","commit_id":"4ea9f5841c407c0d9f32e82e51fd60def83c3860"},{"author":{"_account_id":39146,"name":"Nathaniel Martes","display_name":"Nate Martes","email":"nmartes@NVIDIA.com","username":"nmartes"},"change_message_id":"33ccd9b9646af2bd54a5ffcd9d579938f1154590","unresolved":true,"context_lines":[{"line_number":85,"context_line":"def checksum_sysmeta_headers(checksum_algorithm, checksum_value,"},{"line_number":86,"context_line":"                             checksum_type):"},{"line_number":87,"context_line":"    \"\"\"Return object sysmeta for a calculated or validated checksum.\"\"\""},{"line_number":88,"context_line":"    return {"},{"line_number":89,"context_line":"        s3api_sysmeta_header(\u0027object\u0027, \u0027checksum-algorithm\u0027):"},{"line_number":90,"context_line":"            normalize_checksum_algorithm(checksum_algorithm),"},{"line_number":91,"context_line":"        s3api_sysmeta_header(\u0027object\u0027, \u0027checksum-value\u0027): checksum_value,"}],"source_content_type":"text/x-python","patch_set":6,"id":"f7590ce2_0600bf39","side":"PARENT","line":88,"in_reply_to":"62ef3533_55c1c007","updated":"2026-08-27 05:29:48.000000000","message":"This is better","commit_id":"4ea9f5841c407c0d9f32e82e51fd60def83c3860"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2bd7377e84358203b53ac6c16ba3466f636da3de","unresolved":false,"context_lines":[{"line_number":85,"context_line":"def checksum_sysmeta_headers(checksum_algorithm, checksum_value,"},{"line_number":86,"context_line":"                             checksum_type):"},{"line_number":87,"context_line":"    \"\"\"Return object sysmeta for a calculated or validated checksum.\"\"\""},{"line_number":88,"context_line":"    return {"},{"line_number":89,"context_line":"        s3api_sysmeta_header(\u0027object\u0027, \u0027checksum-algorithm\u0027):"},{"line_number":90,"context_line":"            normalize_checksum_algorithm(checksum_algorithm),"},{"line_number":91,"context_line":"        s3api_sysmeta_header(\u0027object\u0027, \u0027checksum-value\u0027): checksum_value,"}],"source_content_type":"text/x-python","patch_set":6,"id":"d02c2564_29dbc42c","side":"PARENT","line":88,"in_reply_to":"f7590ce2_0600bf39","updated":"2026-08-27 15:16:04.000000000","message":"Done","commit_id":"4ea9f5841c407c0d9f32e82e51fd60def83c3860"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2529f712f5bd134ad4902bcd8fe2830037f0f9b2","unresolved":true,"context_lines":[{"line_number":114,"context_line":"        s3api_sysmeta_header(\u0027object\u0027, \u0027checksum-algorithm\u0027):"},{"line_number":115,"context_line":"            normalize_checksum_algorithm(checksum_algorithm),"},{"line_number":116,"context_line":"    }"},{"line_number":117,"context_line":"    if checksum_value is not None:"},{"line_number":118,"context_line":"        headers[s3api_sysmeta_header("},{"line_number":119,"context_line":"            \u0027object\u0027, \u0027checksum-value\u0027)] \u003d checksum_value"},{"line_number":120,"context_line":"    if checksum_type is not None:"}],"source_content_type":"text/x-python","patch_set":6,"id":"93761945_c634188c","line":117,"updated":"2026-08-26 16:52:23.000000000","message":"the conditional setters can be avoided by using a HeaderKeyDict which ignores None values. But, the tests assertions will need to change to expect title-cased header names.","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"},{"author":{"_account_id":39146,"name":"Nathaniel Martes","display_name":"Nate Martes","email":"nmartes@NVIDIA.com","username":"nmartes"},"change_message_id":"33ccd9b9646af2bd54a5ffcd9d579938f1154590","unresolved":true,"context_lines":[{"line_number":114,"context_line":"        s3api_sysmeta_header(\u0027object\u0027, \u0027checksum-algorithm\u0027):"},{"line_number":115,"context_line":"            normalize_checksum_algorithm(checksum_algorithm),"},{"line_number":116,"context_line":"    }"},{"line_number":117,"context_line":"    if checksum_value is not None:"},{"line_number":118,"context_line":"        headers[s3api_sysmeta_header("},{"line_number":119,"context_line":"            \u0027object\u0027, \u0027checksum-value\u0027)] \u003d checksum_value"},{"line_number":120,"context_line":"    if checksum_type is not None:"}],"source_content_type":"text/x-python","patch_set":6,"id":"a1499e95_3a2613c4","line":117,"in_reply_to":"93761945_c634188c","updated":"2026-08-27 05:29:48.000000000","message":"That can be done","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"},{"author":{"_account_id":39146,"name":"Nathaniel Martes","display_name":"Nate Martes","email":"nmartes@NVIDIA.com","username":"nmartes"},"change_message_id":"be3596e58080e312b104b801368b340a2e862ea0","unresolved":false,"context_lines":[{"line_number":114,"context_line":"        s3api_sysmeta_header(\u0027object\u0027, \u0027checksum-algorithm\u0027):"},{"line_number":115,"context_line":"            normalize_checksum_algorithm(checksum_algorithm),"},{"line_number":116,"context_line":"    }"},{"line_number":117,"context_line":"    if checksum_value is not None:"},{"line_number":118,"context_line":"        headers[s3api_sysmeta_header("},{"line_number":119,"context_line":"            \u0027object\u0027, \u0027checksum-value\u0027)] \u003d checksum_value"},{"line_number":120,"context_line":"    if checksum_type is not None:"}],"source_content_type":"text/x-python","patch_set":6,"id":"14830900_74643f50","line":117,"in_reply_to":"a1499e95_3a2613c4","updated":"2026-08-27 06:36:59.000000000","message":"Done","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2529f712f5bd134ad4902bcd8fe2830037f0f9b2","unresolved":true,"context_lines":[{"line_number":141,"context_line":"        checksum_sysmeta_values(sysmeta_headers)"},{"line_number":142,"context_line":"    if (not checksum_algorithm or not checksum_value or"},{"line_number":143,"context_line":"            (require_type and not checksum_type)):"},{"line_number":144,"context_line":"        return {}"},{"line_number":145,"context_line":"    checksum_header \u003d checksum_header_from_algorithm(checksum_algorithm)"},{"line_number":146,"context_line":"    headers \u003d {checksum_header: checksum_value}"},{"line_number":147,"context_line":"    if checksum_type:"}],"source_content_type":"text/x-python","patch_set":6,"id":"51662c50_8924e3df","line":144,"updated":"2026-08-26 16:52:23.000000000","message":"I feel like it would be easier to just know that Swift always writes down all or none of the 3 sysmeta headers, and not have to remember that one may be missing if we\u0027re looking at an MPU part","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"},{"author":{"_account_id":39146,"name":"Nathaniel Martes","display_name":"Nate Martes","email":"nmartes@NVIDIA.com","username":"nmartes"},"change_message_id":"be3596e58080e312b104b801368b340a2e862ea0","unresolved":false,"context_lines":[{"line_number":141,"context_line":"        checksum_sysmeta_values(sysmeta_headers)"},{"line_number":142,"context_line":"    if (not checksum_algorithm or not checksum_value or"},{"line_number":143,"context_line":"            (require_type and not checksum_type)):"},{"line_number":144,"context_line":"        return {}"},{"line_number":145,"context_line":"    checksum_header \u003d checksum_header_from_algorithm(checksum_algorithm)"},{"line_number":146,"context_line":"    headers \u003d {checksum_header: checksum_value}"},{"line_number":147,"context_line":"    if checksum_type:"}],"source_content_type":"text/x-python","patch_set":6,"id":"ef36dc47_cf63260d","line":144,"in_reply_to":"49ea0405_e685df51","updated":"2026-08-27 06:36:59.000000000","message":"Done","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"},{"author":{"_account_id":39146,"name":"Nathaniel Martes","display_name":"Nate Martes","email":"nmartes@NVIDIA.com","username":"nmartes"},"change_message_id":"33ccd9b9646af2bd54a5ffcd9d579938f1154590","unresolved":true,"context_lines":[{"line_number":141,"context_line":"        checksum_sysmeta_values(sysmeta_headers)"},{"line_number":142,"context_line":"    if (not checksum_algorithm or not checksum_value or"},{"line_number":143,"context_line":"            (require_type and not checksum_type)):"},{"line_number":144,"context_line":"        return {}"},{"line_number":145,"context_line":"    checksum_header \u003d checksum_header_from_algorithm(checksum_algorithm)"},{"line_number":146,"context_line":"    headers \u003d {checksum_header: checksum_value}"},{"line_number":147,"context_line":"    if checksum_type:"}],"source_content_type":"text/x-python","patch_set":6,"id":"49ea0405_e685df51","line":144,"in_reply_to":"51662c50_8924e3df","updated":"2026-08-27 05:29:48.000000000","message":"I think so too...","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2529f712f5bd134ad4902bcd8fe2830037f0f9b2","unresolved":true,"context_lines":[{"line_number":149,"context_line":"    return headers"},{"line_number":150,"context_line":""},{"line_number":151,"context_line":""},{"line_number":152,"context_line":"class ChecksummingInput(InputProxy):"},{"line_number":153,"context_line":"    \"\"\""},{"line_number":154,"context_line":"    wsgi.input wrapper to calculate the X-Amz-Checksum-* of the input as it\u0027s"},{"line_number":155,"context_line":"    read. The calculated value is checked against an expected value that is"}],"source_content_type":"text/x-python","patch_set":6,"id":"01ab5b68_34b803ad","line":152,"range":{"start_line":152,"start_character":6,"end_line":152,"end_character":23},"updated":"2026-08-26 16:52:23.000000000","message":"FWIW I\u0027m happy that this has not changed it\u0027s name, IIRC it did at one point and it felt unnecessary","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"},{"author":{"_account_id":39146,"name":"Nathaniel Martes","display_name":"Nate Martes","email":"nmartes@NVIDIA.com","username":"nmartes"},"change_message_id":"33ccd9b9646af2bd54a5ffcd9d579938f1154590","unresolved":false,"context_lines":[{"line_number":149,"context_line":"    return headers"},{"line_number":150,"context_line":""},{"line_number":151,"context_line":""},{"line_number":152,"context_line":"class ChecksummingInput(InputProxy):"},{"line_number":153,"context_line":"    \"\"\""},{"line_number":154,"context_line":"    wsgi.input wrapper to calculate the X-Amz-Checksum-* of the input as it\u0027s"},{"line_number":155,"context_line":"    read. The calculated value is checked against an expected value that is"}],"source_content_type":"text/x-python","patch_set":6,"id":"e7d66603_1f0f3c03","line":152,"range":{"start_line":152,"start_character":6,"end_line":152,"end_character":23},"in_reply_to":"01ab5b68_34b803ad","updated":"2026-08-27 05:29:48.000000000","message":"Yea we had `CalculateChecksumMetadata` which we really did not need","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"}],"swift/common/middleware/s3api/s3request.py":[{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2529f712f5bd134ad4902bcd8fe2830037f0f9b2","unresolved":true,"context_lines":[{"line_number":1041,"context_line":""},{"line_number":1042,"context_line":"    @property"},{"line_number":1043,"context_line":"    def checksum_sysmeta_footers(self):"},{"line_number":1044,"context_line":"        \"\"\"Return sysmeta-shaped footers for the validated request checksum.\"\"\""},{"line_number":1045,"context_line":"        if self._checksum_sysmeta_footers is not None:"},{"line_number":1046,"context_line":"            return self._checksum_sysmeta_footers"},{"line_number":1047,"context_line":"        # Even if we don\u0027t have any persisted sysmeta,"}],"source_content_type":"text/x-python","patch_set":6,"id":"3edda939_6736626c","line":1044,"range":{"start_line":1044,"start_character":25,"end_line":1044,"end_character":32},"updated":"2026-08-26 16:52:23.000000000","message":"nit: (or maybe not!) I\u0027m guessing this is LLM generated but I have a real dislike for things being \"shaped\". The method return sysmeta footers, end-of.","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"},{"author":{"_account_id":39146,"name":"Nathaniel Martes","display_name":"Nate Martes","email":"nmartes@NVIDIA.com","username":"nmartes"},"change_message_id":"be3596e58080e312b104b801368b340a2e862ea0","unresolved":false,"context_lines":[{"line_number":1041,"context_line":""},{"line_number":1042,"context_line":"    @property"},{"line_number":1043,"context_line":"    def checksum_sysmeta_footers(self):"},{"line_number":1044,"context_line":"        \"\"\"Return sysmeta-shaped footers for the validated request checksum.\"\"\""},{"line_number":1045,"context_line":"        if self._checksum_sysmeta_footers is not None:"},{"line_number":1046,"context_line":"            return self._checksum_sysmeta_footers"},{"line_number":1047,"context_line":"        # Even if we don\u0027t have any persisted sysmeta,"}],"source_content_type":"text/x-python","patch_set":6,"id":"975a768c_20c764d9","line":1044,"range":{"start_line":1044,"start_character":25,"end_line":1044,"end_character":32},"in_reply_to":"054d720c_b3a297fc","updated":"2026-08-27 06:36:59.000000000","message":"Done","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"},{"author":{"_account_id":39146,"name":"Nathaniel Martes","display_name":"Nate Martes","email":"nmartes@NVIDIA.com","username":"nmartes"},"change_message_id":"33ccd9b9646af2bd54a5ffcd9d579938f1154590","unresolved":true,"context_lines":[{"line_number":1041,"context_line":""},{"line_number":1042,"context_line":"    @property"},{"line_number":1043,"context_line":"    def checksum_sysmeta_footers(self):"},{"line_number":1044,"context_line":"        \"\"\"Return sysmeta-shaped footers for the validated request checksum.\"\"\""},{"line_number":1045,"context_line":"        if self._checksum_sysmeta_footers is not None:"},{"line_number":1046,"context_line":"            return self._checksum_sysmeta_footers"},{"line_number":1047,"context_line":"        # Even if we don\u0027t have any persisted sysmeta,"}],"source_content_type":"text/x-python","patch_set":6,"id":"054d720c_b3a297fc","line":1044,"range":{"start_line":1044,"start_character":25,"end_line":1044,"end_character":32},"in_reply_to":"3edda939_6736626c","updated":"2026-08-27 05:29:48.000000000","message":"Yea I think this is more straightforward...\nbut because of the UploadPart requests reutning attributes from `checksumming_input` as sysmeta headers.. maybe \n\n\"\"\"Return sysmeta headers for the request checksum\"\"\"","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2529f712f5bd134ad4902bcd8fe2830037f0f9b2","unresolved":true,"context_lines":[{"line_number":1044,"context_line":"        \"\"\"Return sysmeta-shaped footers for the validated request checksum.\"\"\""},{"line_number":1045,"context_line":"        if self._checksum_sysmeta_footers is not None:"},{"line_number":1046,"context_line":"            return self._checksum_sysmeta_footers"},{"line_number":1047,"context_line":"        # Even if we don\u0027t have any persisted sysmeta,"},{"line_number":1048,"context_line":"        # If we checksummed the object, then we should return the checksum"},{"line_number":1049,"context_line":"        # meta. A case for this is UploadPart when a client did not"},{"line_number":1050,"context_line":"        # specify a checksum on their MPU."}],"source_content_type":"text/x-python","patch_set":6,"id":"403b637d_dc752592","line":1047,"updated":"2026-08-26 16:52:23.000000000","message":"why don\u0027t we just always persist the checksum if we calculated it. If the MPU does not require it then we can just ignore it on the part, right? Keeps it simple.\n\nI\u0027d like to explore this before fixing up the method as per the comments below.","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"},{"author":{"_account_id":39146,"name":"Nathaniel Martes","display_name":"Nate Martes","email":"nmartes@NVIDIA.com","username":"nmartes"},"change_message_id":"33ccd9b9646af2bd54a5ffcd9d579938f1154590","unresolved":true,"context_lines":[{"line_number":1044,"context_line":"        \"\"\"Return sysmeta-shaped footers for the validated request checksum.\"\"\""},{"line_number":1045,"context_line":"        if self._checksum_sysmeta_footers is not None:"},{"line_number":1046,"context_line":"            return self._checksum_sysmeta_footers"},{"line_number":1047,"context_line":"        # Even if we don\u0027t have any persisted sysmeta,"},{"line_number":1048,"context_line":"        # If we checksummed the object, then we should return the checksum"},{"line_number":1049,"context_line":"        # meta. A case for this is UploadPart when a client did not"},{"line_number":1050,"context_line":"        # specify a checksum on their MPU."}],"source_content_type":"text/x-python","patch_set":6,"id":"64836379_c60c14b1","line":1047,"in_reply_to":"403b637d_dc752592","updated":"2026-08-27 05:29:48.000000000","message":"\u003e Keeps it simple\n\nI think this approach could work","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"},{"author":{"_account_id":39146,"name":"Nathaniel Martes","display_name":"Nate Martes","email":"nmartes@NVIDIA.com","username":"nmartes"},"change_message_id":"be3596e58080e312b104b801368b340a2e862ea0","unresolved":false,"context_lines":[{"line_number":1044,"context_line":"        \"\"\"Return sysmeta-shaped footers for the validated request checksum.\"\"\""},{"line_number":1045,"context_line":"        if self._checksum_sysmeta_footers is not None:"},{"line_number":1046,"context_line":"            return self._checksum_sysmeta_footers"},{"line_number":1047,"context_line":"        # Even if we don\u0027t have any persisted sysmeta,"},{"line_number":1048,"context_line":"        # If we checksummed the object, then we should return the checksum"},{"line_number":1049,"context_line":"        # meta. A case for this is UploadPart when a client did not"},{"line_number":1050,"context_line":"        # specify a checksum on their MPU."}],"source_content_type":"text/x-python","patch_set":6,"id":"ecaae3f2_6d3c1fa2","line":1047,"in_reply_to":"64836379_c60c14b1","updated":"2026-08-27 06:36:59.000000000","message":"Done","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2529f712f5bd134ad4902bcd8fe2830037f0f9b2","unresolved":true,"context_lines":[{"line_number":1047,"context_line":"        # Even if we don\u0027t have any persisted sysmeta,"},{"line_number":1048,"context_line":"        # If we checksummed the object, then we should return the checksum"},{"line_number":1049,"context_line":"        # meta. A case for this is UploadPart when a client did not"},{"line_number":1050,"context_line":"        # specify a checksum on their MPU."},{"line_number":1051,"context_line":"        if (self._checksumming_input is None or"},{"line_number":1052,"context_line":"                not self._checksumming_input.matching_checksum):"},{"line_number":1053,"context_line":"            return None"}],"source_content_type":"text/x-python","patch_set":6,"id":"54ec3d33_9a750fb7","line":1050,"updated":"2026-08-26 16:52:23.000000000","message":"I need help understanding this scenario: the client did NOT send a checksum value, Swift did calculate the checksum but Swift did not persist it? why was it calculated, was it purely to return a value in the response?\n\nAre we sure we need to do this, because...\n\"x-amz-checksum-crc32\nThe Base64 encoded, 32-bit CRC32 checksum of the part. This will only be present if the checksum was provided in the request. \" https://docs.aws.amazon.com/AmazonS3/latest/API/API_UploadPart.html\n\nis there a compat test that covers this?\n\nOh, maybe I get it: the upload part did have a checksum but the CreateMultipartUpload did not.\n\nIf we do need this code then can we invert the if so there are less negatives and beef up the comment:\n\n```\ndiff --git a/swift/common/middleware/s3api/s3checksum.py b/swift/common/middleware/s3api/s3checksum.py\nindex 106393e3f..cebba969c 100644\n--- a/swift/common/middleware/s3api/s3checksum.py\n+++ b/swift/common/middleware/s3api/s3checksum.py\n@@ -15,6 +15,7 @@\n \n from hashlib import sha1, sha256\n \n+from swift.common.header_key_dict import HeaderKeyDict\n from swift.common.middleware.s3api.exception import \\\n     S3InputChecksumMismatch, S3InputChecksumTrailerInvalid\n from swift.common.middleware.s3api.utils import s3api_sysmeta_header\ndiff --git a/swift/common/middleware/s3api/s3request.py b/swift/common/middleware/s3api/s3request.py\nindex 50fa224f0..5449dd96b 100644\n--- a/swift/common/middleware/s3api/s3request.py\n+++ b/swift/common/middleware/s3api/s3request.py\n@@ -1044,16 +1044,19 @@ class S3Request(swob.Request):\n         \"\"\"Return sysmeta-shaped footers for the validated request checksum.\"\"\"\n         if self._checksum_sysmeta_footers is not None:\n             return self._checksum_sysmeta_footers\n-        # Even if we don\u0027t have any persisted sysmeta,\n-        # If we checksummed the object, then we should return the checksum\n-        # meta. A case for this is UploadPart when a client did not\n-        # specify a checksum on their MPU.\n-        if (self._checksumming_input is None or\n-                not self._checksumming_input.matching_checksum):\n+        # Even if we don\u0027t have any *persisted* checksum sysmeta, we may have a\n+        # checksum value for the object which we should return in response\n+        # headers. For example, when a CreateMultipartUpload does not specify a\n+        # checksum algorithm but an UploadPart does, then we do not persist the\n+        # part checksum but we should return the checksum value in the\n+        # UploadPart response.\n+        if (self._checksumming_input and\n+                self._checksumming_input.matching_checksum):\n+            return checksum_sysmeta_headers(\n+                self._checksumming_input.algorithm,\n+                self._checksumming_input.matching_checksum)\n+        else:\n             return None\n-        return checksum_sysmeta_headers(\n-            self._checksumming_input.algorithm,\n-            self._checksumming_input.matching_checksum)\n \n     def __init__(self, env, app\u003dNone, conf\u003dNone):\n         # NOTE: app is not used by this class, need for compatibility of S3acl\n\n```","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"},{"author":{"_account_id":39146,"name":"Nathaniel Martes","display_name":"Nate Martes","email":"nmartes@NVIDIA.com","username":"nmartes"},"change_message_id":"33ccd9b9646af2bd54a5ffcd9d579938f1154590","unresolved":true,"context_lines":[{"line_number":1047,"context_line":"        # Even if we don\u0027t have any persisted sysmeta,"},{"line_number":1048,"context_line":"        # If we checksummed the object, then we should return the checksum"},{"line_number":1049,"context_line":"        # meta. A case for this is UploadPart when a client did not"},{"line_number":1050,"context_line":"        # specify a checksum on their MPU."},{"line_number":1051,"context_line":"        if (self._checksumming_input is None or"},{"line_number":1052,"context_line":"                not self._checksumming_input.matching_checksum):"},{"line_number":1053,"context_line":"            return None"}],"source_content_type":"text/x-python","patch_set":6,"id":"ff456178_2a36862a","line":1050,"in_reply_to":"54ec3d33_9a750fb7","updated":"2026-08-27 05:29:48.000000000","message":"Yep this is the right approach!\nHere\u0027s some more context:\n\nWhen it comes to default checksums in the feature, AWS not only echos the checksum the client can send, but it always writes down the CRC64NVME for the part, so in that case it would write down 1 checksum (CRC64NVME) and return the client provided one (SHA1 for instance).\n\nI think this hunk is better too","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2529f712f5bd134ad4902bcd8fe2830037f0f9b2","unresolved":true,"context_lines":[{"line_number":1053,"context_line":"            return None"},{"line_number":1054,"context_line":"        return checksum_sysmeta_headers("},{"line_number":1055,"context_line":"            self._checksumming_input.algorithm,"},{"line_number":1056,"context_line":"            self._checksumming_input.matching_checksum)"},{"line_number":1057,"context_line":""},{"line_number":1058,"context_line":"    def __init__(self, env, app\u003dNone, conf\u003dNone):"},{"line_number":1059,"context_line":"        # NOTE: app is not used by this class, need for compatibility of S3acl"}],"source_content_type":"text/x-python","patch_set":6,"id":"7b299025_a7041d4b","line":1056,"updated":"2026-08-26 16:52:23.000000000","message":"ok but this method is no longer returning ``checksum_sysmeta_footers``","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"},{"author":{"_account_id":39146,"name":"Nathaniel Martes","display_name":"Nate Martes","email":"nmartes@NVIDIA.com","username":"nmartes"},"change_message_id":"33ccd9b9646af2bd54a5ffcd9d579938f1154590","unresolved":true,"context_lines":[{"line_number":1053,"context_line":"            return None"},{"line_number":1054,"context_line":"        return checksum_sysmeta_headers("},{"line_number":1055,"context_line":"            self._checksumming_input.algorithm,"},{"line_number":1056,"context_line":"            self._checksumming_input.matching_checksum)"},{"line_number":1057,"context_line":""},{"line_number":1058,"context_line":"    def __init__(self, env, app\u003dNone, conf\u003dNone):"},{"line_number":1059,"context_line":"        # NOTE: app is not used by this class, need for compatibility of S3acl"}],"source_content_type":"text/x-python","patch_set":6,"id":"ce532d14_272f8c76","line":1056,"in_reply_to":"7b299025_a7041d4b","updated":"2026-08-27 05:29:48.000000000","message":"Maybe we keep `self.checksum_sysmeta_footers` as is and\nmake `self.checksum_sysmeta` which can return the footers if we have them and if not, pull the checksum metadata from `checksumming_input` to handle \n\nI am not totally sold on it but my agents don\u0027t seem to have any other better options\n\nMaybe something better I will try is making a property for checksumming input instead...","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2529f712f5bd134ad4902bcd8fe2830037f0f9b2","unresolved":true,"context_lines":[{"line_number":1588,"context_line":"    def install_checksumming_input("},{"line_number":1589,"context_line":"            self, checksum_hasher, checksum_key, checksum_source):"},{"line_number":1590,"context_line":"        \"\"\""},{"line_number":1591,"context_line":"        Install an input wrapper that validates or calculates a checksum."},{"line_number":1592,"context_line":""},{"line_number":1593,"context_line":"        A checksum source enables validation against a client value. Passing"},{"line_number":1594,"context_line":"        None retains the calculated checksum for controller-owned behavior."}],"source_content_type":"text/x-python","patch_set":6,"id":"f2644cac_d4b3e49e","line":1591,"range":{"start_line":1591,"start_character":38,"end_line":1591,"end_character":61},"updated":"2026-08-26 16:52:23.000000000","message":"``calculates and optionally validates``","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"},{"author":{"_account_id":39146,"name":"Nathaniel Martes","display_name":"Nate Martes","email":"nmartes@NVIDIA.com","username":"nmartes"},"change_message_id":"be3596e58080e312b104b801368b340a2e862ea0","unresolved":false,"context_lines":[{"line_number":1588,"context_line":"    def install_checksumming_input("},{"line_number":1589,"context_line":"            self, checksum_hasher, checksum_key, checksum_source):"},{"line_number":1590,"context_line":"        \"\"\""},{"line_number":1591,"context_line":"        Install an input wrapper that validates or calculates a checksum."},{"line_number":1592,"context_line":""},{"line_number":1593,"context_line":"        A checksum source enables validation against a client value. Passing"},{"line_number":1594,"context_line":"        None retains the calculated checksum for controller-owned behavior."}],"source_content_type":"text/x-python","patch_set":6,"id":"6404c052_0b27a6ee","line":1591,"range":{"start_line":1591,"start_character":38,"end_line":1591,"end_character":61},"in_reply_to":"99783cca_3079c4d9","updated":"2026-08-27 06:36:59.000000000","message":"Done","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"},{"author":{"_account_id":39146,"name":"Nathaniel Martes","display_name":"Nate Martes","email":"nmartes@NVIDIA.com","username":"nmartes"},"change_message_id":"33ccd9b9646af2bd54a5ffcd9d579938f1154590","unresolved":true,"context_lines":[{"line_number":1588,"context_line":"    def install_checksumming_input("},{"line_number":1589,"context_line":"            self, checksum_hasher, checksum_key, checksum_source):"},{"line_number":1590,"context_line":"        \"\"\""},{"line_number":1591,"context_line":"        Install an input wrapper that validates or calculates a checksum."},{"line_number":1592,"context_line":""},{"line_number":1593,"context_line":"        A checksum source enables validation against a client value. Passing"},{"line_number":1594,"context_line":"        None retains the calculated checksum for controller-owned behavior."}],"source_content_type":"text/x-python","patch_set":6,"id":"99783cca_3079c4d9","line":1591,"range":{"start_line":1591,"start_character":38,"end_line":1591,"end_character":61},"in_reply_to":"f2644cac_d4b3e49e","updated":"2026-08-27 05:29:48.000000000","message":"This sounds better","commit_id":"660ad03fc6d576cff3d65d29a08a33ff9bc7ba7b"}],"test/unit/common/middleware/s3api/test_multi_upload.py":[{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2bd7377e84358203b53ac6c16ba3466f636da3de","unresolved":true,"context_lines":[{"line_number":1410,"context_line":"                         put_call.footers)"},{"line_number":1411,"context_line":"        self.assertNotIn(s3api_sysmeta_header(\u0027object\u0027, \u0027checksum-type\u0027),"},{"line_number":1412,"context_line":"                         put_call.footers)"},{"line_number":1413,"context_line":"        put_headers \u003d self.swift.calls_with_headers[-1][2]"},{"line_number":1414,"context_line":"        self.assertNotIn("},{"line_number":1415,"context_line":"            s3api_sysmeta_header(\u0027object\u0027, \u0027checksum-upload-id\u0027), put_headers)"},{"line_number":1416,"context_line":""}],"source_content_type":"text/x-python","patch_set":8,"id":"9ca934db_b0eb1941","line":1413,"updated":"2026-08-27 15:16:04.000000000","message":"this is same as ``put_call.headers``, no need to get ``self.swift.calls_with_headers[-1][2]``","commit_id":"4b7238704985137bbff1b212f8872c094ab950ed"},{"author":{"_account_id":39146,"name":"Nathaniel Martes","display_name":"Nate Martes","email":"nmartes@NVIDIA.com","username":"nmartes"},"change_message_id":"b61e9fb0f50f45febde6f178293d76ed19cf3857","unresolved":false,"context_lines":[{"line_number":1410,"context_line":"                         put_call.footers)"},{"line_number":1411,"context_line":"        self.assertNotIn(s3api_sysmeta_header(\u0027object\u0027, \u0027checksum-type\u0027),"},{"line_number":1412,"context_line":"                         put_call.footers)"},{"line_number":1413,"context_line":"        put_headers \u003d self.swift.calls_with_headers[-1][2]"},{"line_number":1414,"context_line":"        self.assertNotIn("},{"line_number":1415,"context_line":"            s3api_sysmeta_header(\u0027object\u0027, \u0027checksum-upload-id\u0027), put_headers)"},{"line_number":1416,"context_line":""}],"source_content_type":"text/x-python","patch_set":8,"id":"4921a790_40057b3c","line":1413,"in_reply_to":"9ca934db_b0eb1941","updated":"2026-08-27 22:06:56.000000000","message":"Acknowledged","commit_id":"4b7238704985137bbff1b212f8872c094ab950ed"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2bd7377e84358203b53ac6c16ba3466f636da3de","unresolved":true,"context_lines":[{"line_number":1467,"context_line":"                         put_call.footers)"},{"line_number":1468,"context_line":"        self.assertNotIn(s3api_sysmeta_header(\u0027object\u0027, \u0027checksum-type\u0027),"},{"line_number":1469,"context_line":"                         put_call.footers)"},{"line_number":1470,"context_line":"        put_headers \u003d self.swift.calls_with_headers[-1][2]"},{"line_number":1471,"context_line":"        self.assertNotIn("},{"line_number":1472,"context_line":"            s3api_sysmeta_header(\u0027object\u0027, \u0027checksum-upload-id\u0027), put_headers)"},{"line_number":1473,"context_line":""}],"source_content_type":"text/x-python","patch_set":8,"id":"d3b7d233_e294ac59","line":1470,"updated":"2026-08-27 15:16:04.000000000","message":"this is same as ``put_call.headers``, no need to get ``self.swift.calls_with_headers[-1][2]``","commit_id":"4b7238704985137bbff1b212f8872c094ab950ed"},{"author":{"_account_id":39146,"name":"Nathaniel Martes","display_name":"Nate Martes","email":"nmartes@NVIDIA.com","username":"nmartes"},"change_message_id":"b61e9fb0f50f45febde6f178293d76ed19cf3857","unresolved":false,"context_lines":[{"line_number":1467,"context_line":"                         put_call.footers)"},{"line_number":1468,"context_line":"        self.assertNotIn(s3api_sysmeta_header(\u0027object\u0027, \u0027checksum-type\u0027),"},{"line_number":1469,"context_line":"                         put_call.footers)"},{"line_number":1470,"context_line":"        put_headers \u003d self.swift.calls_with_headers[-1][2]"},{"line_number":1471,"context_line":"        self.assertNotIn("},{"line_number":1472,"context_line":"            s3api_sysmeta_header(\u0027object\u0027, \u0027checksum-upload-id\u0027), put_headers)"},{"line_number":1473,"context_line":""}],"source_content_type":"text/x-python","patch_set":8,"id":"3e4d8cda_180d29d6","line":1470,"in_reply_to":"d3b7d233_e294ac59","updated":"2026-08-27 22:06:56.000000000","message":"Acknowledged","commit_id":"4b7238704985137bbff1b212f8872c094ab950ed"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2bd7377e84358203b53ac6c16ba3466f636da3de","unresolved":true,"context_lines":[{"line_number":1523,"context_line":"            s3api_sysmeta_header(\u0027object\u0027, \u0027checksum-value\u0027)])"},{"line_number":1524,"context_line":"        self.assertEqual(\u0027FULL_OBJECT\u0027, put_call.footers["},{"line_number":1525,"context_line":"            s3api_sysmeta_header(\u0027object\u0027, \u0027checksum-type\u0027)])"},{"line_number":1526,"context_line":"        put_headers \u003d self.swift.calls_with_headers[-1][2]"},{"line_number":1527,"context_line":"        self.assertEqual(\u0027X\u0027, put_headers.get("},{"line_number":1528,"context_line":"            s3api_sysmeta_header(\u0027object\u0027, \u0027checksum-upload-id\u0027)))"},{"line_number":1529,"context_line":""}],"source_content_type":"text/x-python","patch_set":8,"id":"894e2330_2944d832","line":1526,"updated":"2026-08-27 15:16:04.000000000","message":"this is same as ``put_call.headers``, no need to get ``self.swift.calls_with_headers[-1][2]``","commit_id":"4b7238704985137bbff1b212f8872c094ab950ed"},{"author":{"_account_id":39146,"name":"Nathaniel Martes","display_name":"Nate Martes","email":"nmartes@NVIDIA.com","username":"nmartes"},"change_message_id":"b61e9fb0f50f45febde6f178293d76ed19cf3857","unresolved":false,"context_lines":[{"line_number":1523,"context_line":"            s3api_sysmeta_header(\u0027object\u0027, \u0027checksum-value\u0027)])"},{"line_number":1524,"context_line":"        self.assertEqual(\u0027FULL_OBJECT\u0027, put_call.footers["},{"line_number":1525,"context_line":"            s3api_sysmeta_header(\u0027object\u0027, \u0027checksum-type\u0027)])"},{"line_number":1526,"context_line":"        put_headers \u003d self.swift.calls_with_headers[-1][2]"},{"line_number":1527,"context_line":"        self.assertEqual(\u0027X\u0027, put_headers.get("},{"line_number":1528,"context_line":"            s3api_sysmeta_header(\u0027object\u0027, \u0027checksum-upload-id\u0027)))"},{"line_number":1529,"context_line":""}],"source_content_type":"text/x-python","patch_set":8,"id":"d8498a35_66746c8b","line":1526,"in_reply_to":"894e2330_2944d832","updated":"2026-08-27 22:06:56.000000000","message":"Acknowledged","commit_id":"4b7238704985137bbff1b212f8872c094ab950ed"}]}
