GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — gh-129559: Add `bytearray.resize()` by cmaloney · Pull Request #129560 · python/cpython

Conversation

@cmaloney

@cmaloney cmaloney commented Feb 1, 2025 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

Add bytearray.resize() which wraps PyByteArray_Resize.

Make negative size passed to resize exception/error rather than crash in optimized builds.

  • Issue: Add bytearray.resize() method #129559

📚 Documentation preview 📚: https://cpython-previews--129560.org.readthedocs.build/

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@cmaloney
pythongh-129559: Add bytearray.resize()
e240327
Add `bytearray.resize()` which wraps `PyByteArray_Resize`
@bedevere-app bedevere-app Bot added the awaiting review label Feb 1, 2025
@bedevere-app bedevere-app Bot mentioned this pull request Feb 1, 2025
Add bytearray.resize() method #129559
Closed
@cmaloney cmaloney mentioned this pull request Feb 1, 2025
gh-129005: Fix buffer expansion in _pyio.FileIO.readall #129541
Closed
cmaloney added 9 commits February 1, 2025 15:46
@cmaloney
Add versionchanged note to c-api around len behavior
925fcbd
@cmaloney
Add argument formatting to stdtypes.rst
29c04ad
@cmaloney
Add buffer error to c api test
d15ef67
@cmaloney
Remove case that no longer crashes
d199032
@cmaloney
Fix versionchanged
18829a2
@cmaloney
Fix doc warnings
df779e2
@cmaloney
Fix requested size check range
facc91f
@cmaloney
0 is a fine size
d8b9faf
@cmaloney
Fix grammar
7429cf4
@cmaloney

cmaloney commented Feb 2, 2025

Copy link
Copy Markdown
Contributor Author

@vstinner I think this is ready for review if bytearray.resize seems like a reasonable feature add.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

vstinner
vstinner reviewed Feb 2, 2025
View reviewed changes
Comment thread Lib/test/test_bytes.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

vstinner
vstinner reviewed Feb 2, 2025
View reviewed changes
Comment thread Doc/library/stdtypes.rst Resolved

Uh oh!

There was an error while loading. Please reload this page.

@cmaloney
Add NULL byte tests, include set bytes in docs
c183116
cmaloney
cmaloney commented Feb 2, 2025
View reviewed changes
Comment thread Objects/bytearrayobject.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

Viicos
Viicos reviewed Feb 2, 2025
View reviewed changes
Comment thread Doc/library/stdtypes.rst Resolved

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Feb 2, 2025

Copy link
Copy Markdown
Member

Please initialize new bytes to zero. We don't accept undefined behavior in Python.

cmaloney and gpshead reacted with thumbs up emoji
All reactions
  • 👍 2 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

cmaloney and others added 2 commits February 2, 2025 09:55
@cmaloney @Viicos
Update Doc/library/stdtypes.rst
dd46a85
Co-authored-by: Victorien <[email protected]>
@cmaloney
Always null new bytes
69bbdc1
vstinner
vstinner reviewed Feb 2, 2025
View reviewed changes
Comment thread Objects/bytearrayobject.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Lib/test/test_bytes.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Lib/test/test_bytes.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Objects/bytearrayobject.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Doc/library/stdtypes.rst Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Lib/test/test_bytes.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Feb 3, 2025

Copy link
Copy Markdown
Member

I'm not sure that adding this API is needed since there is already a way to truncate a bytearray and to extend a bytearray using the existing API. You should run a benchmark to show that it's worth it, especially to extend a bytearray.

cmaloney reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@cmaloney

cmaloney commented Feb 3, 2025

Copy link
Copy Markdown
Contributor Author

What is the way to extend the bytearray without copying byte by byte or having the length of the extension extra memory allocated?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Feb 4, 2025

Copy link
Copy Markdown
Member

What is the way to extend the bytearray without copying byte by byte or having the length of the extension extra memory allocated?

What you say. In short, ba[len(ba):] = b'\0' * extend.

Please run a benchmark to measure this compared to ba.extend().

cmaloney reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

ZeroIntensity
ZeroIntensity reviewed Feb 4, 2025
View reviewed changes

@ZeroIntensity ZeroIntensity left a comment

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The implementation mostly looks fine, but my primary concern is that we are indeed exposing an implementation detail on our end. Alternate Python implementations that either don't use an internal buffer, or can't control the size of it, will have a hard time effectively providing resize.

For example, Brython implements bytearray using JavaScript arrays, which don't have an explicit way to resize. I don't think they'll like this method.

I'd go for one of two options:

  • Mark resize as a CPython implementation detail.
  • Make resize a hint, allowing the runtime to ignore the request for resizing if it wants to.

I'd personally choose the latter, but I'd also like to hear Victor's thoughts.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread Objects/bytearrayobject.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Objects/bytearrayobject.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Doc/c-api/bytearray.rst Resolved

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Feb 4, 2025

Copy link
Copy Markdown
Member

@ZeroIntensity: It's already possible to implement extend() with the current bytearray API, it's just a convenient helper for: ba[len(ba):] = b'\0' * extend. So I don't see why other Python implementation couldn't implement it. They should already be able to resize a bytearray.

There are many ways to "extend" a bytearray. Another example:

>>> ba=bytearray(b'abc')
>>> ba += b'def'
>>> ba, len(ba)
(bytearray(b'abcdef'), 6)
cmaloney reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ZeroIntensity

ZeroIntensity commented Feb 4, 2025

Copy link
Copy Markdown
Member

Yeah, but I think that's a bit different than an explicit "resize." You can emulate it with that kind of thing, but it feels different than an actual method saying it will reallocate.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Feb 4, 2025

Copy link
Copy Markdown
Member

The method documentation should not promise any performance guarantee in general, it can be implemented in different ways (the implementation doesn't matter).

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ZeroIntensity

ZeroIntensity commented Feb 4, 2025

Copy link
Copy Markdown
Member

Can we clarify that with a CPython implementation detail note?

cmaloney reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@cmaloney

cmaloney commented Feb 4, 2025 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor Author

@ZeroIntensity can definitely add a note of "This is equivalent to bytearray += b'\0' * size". For more efficiency in JavaScript in particular I think this could map to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/fill -- nevermind, just sets elements to 0, can't append.

(Planning to do next iteration + perf testing follow up today, yesterday got sucked into a BytesIO.readfrom(file, *, limit: int | None = None, expected: int | None = None) experiment)

ZeroIntensity reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

cmaloney and others added 2 commits February 4, 2025 10:23
@cmaloney @ZeroIntensity @vstinner
Apply suggestions from code review
ec4aa3d
Co-authored-by: Peter Bierma <[email protected]>
Co-authored-by: Victor Stinner <[email protected]>
@cmaloney @vstinner
Update Objects/bytearrayobject.c
336299a
Co-authored-by: Victor Stinner <[email protected]>
@vstinner

vstinner commented Feb 4, 2025

Copy link
Copy Markdown
Member

test_bytes and test_capi are failing, you need to update the exception.

cmaloney reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@cmaloney

cmaloney commented Feb 4, 2025

Copy link
Copy Markdown
Contributor Author

Working on them; Unfortunate part of "add suggestions", it adds "this person contributed" but doesn't let you do that + local changes before making push visible / alerting reviewers... Should have soon

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

cmaloney added 2 commits February 4, 2025 13:03
@cmaloney
tweak docs, fixup return exception
1a0e157
@cmaloney
Update test_resize_forbidden to use bytearray.resize
cf89ec1
gpshead
gpshead reviewed Feb 5, 2025
View reviewed changes
Comment thread Doc/c-api/bytearray.rst Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Doc/library/stdtypes.rst Outdated
.. method:: resize(size)

Resize the :class:`bytearray` to contain *size* bytes.
If :class:`bytearray` needs to grow, all new bytes will be set to null bytes.

@gpshead gpshead Feb 5, 2025

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also describe the "obvious-to-us" behavior of what happens when it shrinks: the data at the end is truncated, the remaining data should be equivalent to a [:size] slice.

... and consider if the Python version should also support negative size to mean the same thing it would in a slice notation.
if (size < 0): size = max(0, len(self) + size) ?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@cmaloney cmaloney Feb 5, 2025 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 for the truncation.

I don't like negative, because the function operates in absolute buffer size, and requesting a negative buffer size sounds like a bug I'd write and I'd prefer an exception / exit rather than debug the symptom "my buffer shrank".

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@cmaloney cmaloney Feb 5, 2025

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my original equivalent to is wrong, which make it look like a delta, current doc one I'm validating:

if len(self) > size:
    del self[size:]
else:
    self += b'\0' * (size - len(self))

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread Lib/test/test_bytes.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Doc/library/stdtypes.rst Resolved

Uh oh!

There was an error while loading. Please reload this page.

@cmaloney
doc tweaks
ddc7b09
@cmaloney

cmaloney commented Feb 5, 2025 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor Author

I think I've addressed all review comments.

Tested performance on an optimized build (--with-lto --enable-optimizations)

$ python bm_bytearray_resize.py       
.....................
extend_bytes: Mean +- std dev: 142 ms +- 1 ms
.....................
extend_range: Mean +- std dev: 1.89 sec +- 0.07 sec
.....................
iadd: Mean +- std dev: 143 ms +- 1 ms
.....................
resize: Mean +- std dev: 48.7 ms +- 0.5 ms
import itertools
import pyperf

runner = pyperf.Runner()


# Aiming for what a file readall does in terms of resizing as get more data.
# Non-linear resizing up to a reaonably large read size.
blocksize = 1024
sizes = [
    blocksize,
    blocksize * 4,
    blocksize * 16,
    1_000_000,
    100_000_000,
    1_000_000_000,
    ]

def extend_bytes():
    """Extend by appending a temporary byte string"""
    ba = bytearray()
    for size in sizes:
        ba.extend(b'\0' * size)


def extend_range():
    """Resize by an iterator with length_hint"""
    ba = bytearray()
    for size in sizes:
        ba.extend(itertools.repeat(0, size))


def iadd():
    """Resize by using += """
    ba = bytearray()
    for size in sizes:
        ba += b'\0' * size


def resize():
    """Use resize"""
    ba = bytearray()
    for size in sizes:
        ba.resize(size)



runner.bench_func("extend_bytes", extend_bytes)
runner.bench_func("extend_range", extend_range)
runner.bench_func("iadd", iadd)
runner.bench_func("resize", resize)
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

cmaloney added 2 commits February 4, 2025 22:58
@cmaloney
Fix indentation of equivalent code
a49374b
@cmaloney
Fix doctest
298d052
vstinner
vstinner reviewed Feb 5, 2025
View reviewed changes
Comment thread Doc/library/stdtypes.rst Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Doc/library/stdtypes.rst Outdated
Comment on lines +2863 to +2870
>>> shrink = bytearray(5)
>>> resize(shrink, 0)
>>> shrink
bytearray(b'')
>>> grow = bytearray(2)
>>> resize(grow, 7)
>>> grow
bytearray(b'\x00\x00\x00\x00\x00\x00\x00')

@vstinner vstinner Feb 5, 2025

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to use resize in examples:

Suggested change
>>> shrink = bytearray(5)
>>> resize(shrink, 0)
>>> shrink
bytearray(b'')
>>> grow = bytearray(2)
>>> resize(grow, 7)
>>> grow
bytearray(b'\x00\x00\x00\x00\x00\x00\x00')
Examples:
>>> shrink = bytearray(b'abc')
>>> shrink.resize(1)
>>> (shrink, len(shrink))
(bytearray(b'a'), 1)
>>> grow = bytearray(b'abc')
>>> grow.resize(5)
>>> (grow, len(grow))
(bytearray(b'abc\x00\x00'), 5)

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

cmaloney reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

@cmaloney cmaloney Feb 5, 2025

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Making locally so I can get the doctest passing)

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread Lib/test/test_bytes.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Misc/NEWS.d/next/Library/2025-02-01-14-55-33.gh-issue-129559.hQCeAz.rst Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Objects/bytearrayobject.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Doc/c-api/bytearray.rst Resolved

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Feb 5, 2025

Copy link
Copy Markdown
Member

The change mostly LGTM. I just made a new review for some details.

extend_bytes: Mean +- std dev: 142 ms +- 1 ms
extend_range: Mean +- std dev: 1.89 sec +- 0.07 sec
iadd: Mean +- std dev: 143 ms +- 1 ms
resize: Mean +- std dev: 48.7 ms +- 0.5 ms

Thanks for the benchmark. So resize is 2.9x faster than the existing fastest method to grow a bytearray (extend_bytes). So it's worth it to add a dedicated method.

cmaloney reacted with rocket emoji
All reactions
  • 🚀 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

cmaloney and others added 4 commits February 5, 2025 09:42
@cmaloney @vstinner
Apply suggestions from code review
2f6b0a3
Co-authored-by: Victor Stinner <[email protected]>
@cmaloney
Update doctest per review
bf00f33
@cmaloney
Fix whitespace in test_bytes
8df7b02
@cmaloney
Merge branch 'main' into resize_pr
43e46dd
@gpshead

gpshead commented Feb 5, 2025

Copy link
Copy Markdown
Member

Nice, I'm not surprised it is faster. Though what I care more about is that code using this is more readable than the other hacks. :)

cmaloney reacted with hooray emoji vstinner reacted with heart emoji vstinner reacted with rocket emoji
All reactions
  • 🎉 1 reaction
  • ❤️ 1 reaction
  • 🚀 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

gpshead
gpshead approved these changes Feb 5, 2025
View reviewed changes
@bedevere-app bedevere-app Bot added awaiting merge and removed awaiting review labels Feb 5, 2025
@gpshead
gpshead merged commit 5fb019f into python:main Feb 5, 2025
@bedevere-app bedevere-app Bot removed the awaiting merge label Feb 5, 2025
@cmaloney
cmaloney deleted the resize_pr branch February 5, 2025 20:03
@cmaloney cmaloney mentioned this pull request Feb 5, 2025
Convert change detection to a Python script #129627
Merged
srinivasreddy pushed a commit to srinivasreddy/cpython that referenced this pull request Feb 7, 2025
@cmaloney @srinivasreddy
pythongh-129559: Add bytearray.resize() (pythonGH-129560)
eeecb1c
Add bytearray.resize() which wraps PyByteArray_Resize.

Make negative size passed to resize exception/error rather than crash in optimized builds.
cmaloney added a commit to cmaloney/cpython that referenced this pull request Feb 8, 2025
@cmaloney
pythongh-129559: Add bytearray.resize() (pythonGH-129560)
0eebecf
Add bytearray.resize() which wraps PyByteArray_Resize.

Make negative size passed to resize exception/error rather than crash in optimized builds.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@vstinner vstinner vstinner left review comments
@ZeroIntensity ZeroIntensity ZeroIntensity left review comments
@gpshead gpshead gpshead approved these changes
+1 more reviewer
@Viicos Viicos Viicos left review comments
Reviewers whose approvals may not affect merge requirements

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

5 participants

@cmaloney @vstinner @ZeroIntensity @gpshead @Viicos
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.