-
Notifications
You must be signed in to change notification settings - Fork 283
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
Factor out IPythonBuffer from IBufferProtocol #808
Conversation
In the first commit I separated the buffer API from the buffer exporter API. This is how PEP 3118 handles it too. The difference is how the buffer is being released. PEP 3118 argues against an idea of a separate releaser object (that is, different than the exported object), and that argument makes sense for C, but with OO C#, the
The changes here deliberately stop short from changing the existing behaviour in any big way. In particular, I think that several methods in In CPython, a bytes-like handling function accepts the exporter object as argument (i.e. an object that has A workaround could be to release the buffer in the conversion ET right after the desired data object has been obtained, but this is not as the buffer protocol should work. The data object will be used after the buffer is released. It will work sort-of OK with IronPython built-in types that implement IBufferProtocol, but could be disastrous for unmanaged/unsafe 3-rd party libraries like NumPy. |
This is a good start. Hopefully I can find some time to experiment with this. Here are some of my initial thoughts:
Agreed, most of them can probably go.
Well, maybe we should revisit this. While these types are more from a .NET point of view it seems like they're not a great fit for the Python world. In particular We could have |
We can revisit it, but I wouldn't dismiss the existing mechanism as yet. First of all, nothing needs to change to support Using
Using
I agree that So now how to phase it out. Since the full progression of types is |
This seems like a good first step, however, before jumping into it, I'd like to make sure this actually works. In particular, I'm trying to see how we would get from, for example, an array a = new array("i", new byte[] {1,2,3,4,5});
using IPythonBuffer buffer = a.GetBuffer();
ReadOnlyMemory<byte> mem = buffer.ToMemory(); The issues which we would need to resolve are:
I guess in all the bytes(array.array('i', [1, 2, 3, 4, 5])) Any ideas? |
Here are my ideas (none of them tested out, though). First of all, although As for the Yes, it would be easier with spans, but we can't use spans in public methods. So I was thinking about replacing An alternative: forget the generics and always return This does not solve the Yet another idea: use |
Here is another idea that I have tested out a little bit. It addresses the array problem locally, in public T this[int index] {
get => MemoryMarshal.Cast<byte, T>(_items.Span)[index];
set => MemoryMarshal.Cast<byte, T>(_items.Span)[index] = value;
} I would like to see it tied out. @slozier, you have been working on |
Agreed, these are the methods I would also remove. I'm not sure that they are even needed as extension methods as their (only?) consumer is memoryview.
I've thought about that as well, but while it solves the issue with
This is the idea I'm currently leaning towards. In fact, if we do this then I don't see why we couldn't just get rid of |
I remember that having span as a method parameter of a public method was not working well. I never fully understood the root cause of this bug so I was avoiding spans anywhere public. But depending on the nature of the bug, maybe it is OK to have I am currently testing it. |
Part of the issue with |
This was originally "Remove IPythonBuffer.SetItem(...)" but things turned out to be quite interdependent. I tried to keep changes to minimum, just to the level of all tests passing, and commit them to get early feedback on the direction it is going in. The code is not proofread or cleaned up, and is sprinkled with TODO comments. There are some additional tests that are passing now (not included) but to my knowledge there are no regressions. It is strange that the build fails; does CI do something that a local |
I will try to merge in master and see if it helps. |
I like it. I added an |
@slozier, thanks for the commit. I started working on However, obtaining and releasing (and in the case of Further, I am not sure about calling Handling unformatted buffers is not quite clear to me. I find Python documentation on this point incomplete and ambiguous. Maybe I will have to look into CPython's code in the end to get the full picture. But one thing that is clear is that exporters, when given requests without |
I wonder if there's any value in making the Personally I don't see the issue with calling |
return Bytes.Empty; | ||
} | ||
|
||
var buf = _buffer.AsReadOnlySpan(); | ||
var buf = _buffer!.AsReadOnlySpan(); |
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.
Is there a smart way to indicate _buffer
being not null after the CheckBuffer()
call, withouth changing the signature of CheckBuffer()
?
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 believe MemberNotNullAttribute would allow that, unfortunately it's still considered a preview feature.
@@ -396,7 +396,7 @@ public sealed class MemoryView : ICodeFormattable, IWeakReferenceable, IBufferPr | |||
return cast(format, null); | |||
} | |||
|
|||
public MemoryView cast([NotNull]string format, [NotNull]object shape) { | |||
public MemoryView cast([NotNull]string format, [NotNull, AllowNull]object shape) { |
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.
[NotNull, AllowNull]
— I find it not very readable. Not to say the confusion with NotNull
from System.Diagnostics.CodeAnalysis
. How about adopting a convention to always using
-renaming Microsoft.Scripting.Runtime.NotNullAttribute
to NotNoneAttribute
?
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 be fine with NotNone
if there is a conflict.
using NotNoneAttribute = Microsoft.Scripting.Runtime.NotNullAttribute;
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.
This is great! A few things I noted (for other PRs if any):
BytesIO.getbuffer
should be reworked to not return a copy- Does
CData
work? (very low priority) - Should
MemoryView
be anIPythonBuffer
?
} | ||
|
||
int newStart = _start + start * firstIndexWidth; | ||
int newEnd = _start + stop * firstIndexWidth; | ||
int newStep = ((long)_step * step).ClampToInt32(); |
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 guess we can get rid of PythonOps.ClampToInt32
now? 😏
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.
Agreed. I think there are more parts in
To my knowledge,
Good point, but debatable. The way I see it IPythonBuffer buf = memoryView.GetBuffer();
// somewhere else, perhaps in a sub-method:
if (buf is MemoryView mv) {
// have a feast...
} but I also don't feel strongly of adding extra complexity just to prevent it. On my side, besides the remaining TODOs, I see the following:
|
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'm happy with this, feel free to merge when ready.
Here is my exploration around the re-implementation of the buffer protocol (#21). I will try to make incremental changes that will keep IronPython working (so all tests should be passing).
Each step is a proposal of an idea that can be discussed and adjusted, so smaller steps are preferable, rather a large dive that goes nowhere.
The PR is writable to the maintainers, so I welcome commits on top in a collaborative effort.