Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes for strict-bytes #10454

Merged
merged 8 commits into from
Feb 28, 2025
Merged

Fixes for strict-bytes #10454

merged 8 commits into from
Feb 28, 2025

Conversation

Dreamsorcerer
Copy link
Member

This will be default behaviour in mypy 2, so best to fix it early.

It would be good if anybody else can check over these, the existing code seems a little confused with when it wants bytes only or will allow bytes/bytearray/memoryview.

@Dreamsorcerer Dreamsorcerer added bot:chronographer:skip This PR does not need to include a change note backport:skip Skip backport bot labels Feb 11, 2025
@Dreamsorcerer
Copy link
Member Author

I'm unclear if the remaining errors are a bug with us or with typeshed (which says the method doesn't support memoryview[bytes]).

Copy link

codecov bot commented Feb 11, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 98.69%. Comparing base (7d77e12) to head (5b3337a).
Report is 3 commits behind head on master.

✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##           master   #10454   +/-   ##
=======================================
  Coverage   98.69%   98.69%           
=======================================
  Files         122      122           
  Lines       37225    37230    +5     
  Branches     2063     2064    +1     
=======================================
+ Hits        36740    36745    +5     
  Misses        338      338           
  Partials      147      147           
Flag Coverage Δ
CI-GHA 98.57% <95.65%> (-0.01%) ⬇️
OS-Linux 98.24% <95.65%> (-0.01%) ⬇️
OS-Windows 96.17% <95.65%> (-0.01%) ⬇️
OS-macOS 97.36% <95.65%> (+<0.01%) ⬆️
Py-3.10.11 97.26% <91.30%> (-0.01%) ⬇️
Py-3.10.16 97.81% <91.30%> (-0.01%) ⬇️
Py-3.11.11 97.89% <91.30%> (-0.01%) ⬇️
Py-3.11.9 97.35% <91.30%> (-0.01%) ⬇️
Py-3.12.8 97.14% <86.95%> (?)
Py-3.12.9 98.33% <86.95%> (-0.03%) ⬇️
Py-3.13.1 97.14% <86.95%> (-0.01%) ⬇️
Py-3.13.2 98.31% <86.95%> (-0.01%) ⬇️
Py-3.9.13 97.14% <91.30%> (-0.01%) ⬇️
Py-3.9.21 97.68% <91.30%> (-0.01%) ⬇️
Py-pypy7.3.16 97.28% <91.30%> (+9.17%) ⬆️
VM-macos 97.36% <95.65%> (+<0.01%) ⬆️
VM-ubuntu 98.24% <95.65%> (-0.01%) ⬇️
VM-windows 96.17% <95.65%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link

codspeed-hq bot commented Feb 11, 2025

CodSpeed Performance Report

Merging #10454 will not alter performance

Comparing strict-bytes (5b3337a) with master (7d77e12)

Summary

✅ 47 untouched benchmarks

@asvetlov
Copy link
Member

Would you backport the PR

@Dreamsorcerer
Copy link
Member Author

Would you backport the PR

It changes the (apparent) API, so I'd prefer to avoid it. Hopefully will be focusing on v4 after the typing work I'm doing.

@asvetlov
Copy link
Member

Up to you, any decision is good to me.

@asvetlov
Copy link
Member

asvetlov commented Feb 11, 2025

Yes, focusing on V4 would be good.

Personally, I should review the diff and file a list of required changes related to my commits on master that were not backported.
I have quite a tight schedule this month; but the task is in my to-do list.

@Dreamsorcerer
Copy link
Member Author

I'm unclear if the remaining errors are a bug with us or with typeshed (which says the method doesn't support memoryview[bytes]).

@bdraco I think you've played with that code fairly recently, any idea if memoryview[bytes] is actually supported?

@bdraco
Copy link
Member

bdraco commented Feb 20, 2025

asyncio Transport write is typed to (method) def write(data: bytes | bytearray | memoryview[int]) -> Any

so I think only memoryview[int] is needed.

@Dreamsorcerer
Copy link
Member Author

asyncio Transport write is typed to (method) def write(data: bytes | bytearray | memoryview[int]) -> Any

so I think only memoryview[int] is needed.

We have tests that explicitly use memoryview[bytes] though. So, there's either a bug in our code, or in typeshed.

@bdraco
Copy link
Member

bdraco commented Feb 20, 2025

I couldn't find any tests where we have memoryview[bytes] .. AFAICT memoryview(b"") has a type of memoryview[int]....

def memoryview_bytes(x: "memoryview[bytes]") -> None:
    pass


def memoryview_int(x: "memoryview[int]") -> None:
    pass


v = memoryview(b"")

memoryview_int(v)  # pass

memoryview_bytes(
    v
)  # error: Argument 1 to "memoryview_bytes" has incompatible type "memoryview[int]"; expected "memoryview[bytes]"  [arg-type]

@Dreamsorcerer
Copy link
Member Author

I couldn't find any tests where we have memoryview[bytes] .. AFAICT memoryview(b"") has a type of memoryview[int]....

If you drop bytes from the union, then mypy will flag it up.

@Dreamsorcerer
Copy link
Member Author

tests/test_http_writer.py:454:21: error: Argument 1 to "write" of
"StreamWriter" has incompatible type "memoryview[bytes]"; expected
"bytes | bytearray | memoryview[int]"  [arg-type]
        await msg.write(payload)
                        ^~~~~~~
tests/test_http_writer.py:473:21: error: Argument 1 to "write" of
"StreamWriter" has incompatible type "memoryview[bytes]"; expected
"bytes | bytearray | memoryview[int]"  [arg-type]
        await msg.write(payload)
                        ^~~~~~~

async def test_write_payload_2d_shape_memoryview(
buf: bytearray,
protocol: BaseProtocol,
transport: asyncio.Transport,
loop: asyncio.AbstractEventLoop,
) -> None:
msg = http.StreamWriter(protocol, loop)
msg.enable_chunking()
mv = memoryview(b"ABCDEF")
payload = mv.cast("c", [3, 2])
await msg.write(payload)
await msg.write_eof()
thing = b"6\r\nABCDEF\r\n0\r\n\r\n"
assert thing == buf
async def test_write_payload_slicing_long_memoryview(
buf: bytearray,
protocol: BaseProtocol,
transport: asyncio.Transport,
loop: asyncio.AbstractEventLoop,
) -> None:
msg = http.StreamWriter(protocol, loop)
msg.length = 4
mv = memoryview(b"ABCDEF")
payload = mv.cast("c", [3, 2])
await msg.write(payload)
await msg.write_eof()
thing = b"ABCD"
assert thing == buf

@bdraco
Copy link
Member

bdraco commented Feb 20, 2025

payload = mv.cast("c", [3, 2])
That's what I was missing

def memoryview_bytes(x: "memoryview[bytes]") -> None:
    pass


def memoryview_int(x: "memoryview[int]") -> None:
    pass


v = memoryview(b"")

memoryview_int(v)  # pass

payload = v.cast("c", [3, 2])
memoryview_bytes(payload)

passes now

@bdraco
Copy link
Member

bdraco commented Feb 20, 2025

tests/test_http_writer.py:454:21: error: Argument 1 to "write" of
"StreamWriter" has incompatible type "memoryview[bytes]"; expected
"bytes | bytearray | memoryview[int]"  [arg-type]
        await msg.write(payload)
                        ^~~~~~~
tests/test_http_writer.py:473:21: error: Argument 1 to "write" of
"StreamWriter" has incompatible type "memoryview[bytes]"; expected
"bytes | bytearray | memoryview[int]"  [arg-type]
        await msg.write(payload)
                        ^~~~~~~

async def test_write_payload_2d_shape_memoryview(
buf: bytearray,
protocol: BaseProtocol,
transport: asyncio.Transport,
loop: asyncio.AbstractEventLoop,
) -> None:
msg = http.StreamWriter(protocol, loop)
msg.enable_chunking()
mv = memoryview(b"ABCDEF")
payload = mv.cast("c", [3, 2])
await msg.write(payload)
await msg.write_eof()
thing = b"6\r\nABCDEF\r\n0\r\n\r\n"
assert thing == buf
async def test_write_payload_slicing_long_memoryview(
buf: bytearray,
protocol: BaseProtocol,
transport: asyncio.Transport,
loop: asyncio.AbstractEventLoop,
) -> None:
msg = http.StreamWriter(protocol, loop)
msg.length = 4
mv = memoryview(b"ABCDEF")
payload = mv.cast("c", [3, 2])
await msg.write(payload)
await msg.write_eof()
thing = b"ABCD"
assert thing == buf

So it seems like we support it but asyncio is typed to only memoryview[int] since we end up calling write or writelines at

transport.write(b"".join(chunks))
transport.writelines(chunks)

def writelines(list_of_data: Iterable[bytes | bytearray | memoryview[int]]) -> None
def write(data: bytes | bytearray | memoryview[int]) -> None

I guess it works though since #4890 seems to use it

@Dreamsorcerer
Copy link
Member Author

I guess it works though since #4890 seems to use it

Yeah, so typeshed bug then, I'll file a PR there.

@bdraco
Copy link
Member

bdraco commented Feb 20, 2025

It looks like it gets cast to c (char)

chunk = chunk.cast("c")

but c is still memoryview[bytes]...

So maybe asyncio only supports memoryview[int], but memoryview[bytes] happens to work and could break anytime....

@Dreamsorcerer
Copy link
Member Author

Typeshed updated to memoryview[object].

@Dreamsorcerer Dreamsorcerer enabled auto-merge (squash) February 28, 2025 14:41
@Dreamsorcerer Dreamsorcerer merged commit a5f5205 into master Feb 28, 2025
37 checks passed
@Dreamsorcerer Dreamsorcerer deleted the strict-bytes branch February 28, 2025 14:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backport:skip Skip backport bot bot:chronographer:skip This PR does not need to include a change note
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants