ironpython3
ironpython3 copied to clipboard
Multiple targets could match in write function of io.BytesIO subclass
For example:
import io
class Test(io.BytesIO): pass
x = Test()
x.write(b"")
results in a
Multiple targets could match: write(object), write(IBufferProtocol)
I'm guessing it's because the object overload is an override of a virtual? @BCSharp any idea?
Comes from test_gzip (see https://github.com/IronLanguages/ironpython3/pull/1071).
Also affects truncate
and readinto
(and probably other io type/method combinations):
import io
class Test(io.BytesIO): pass
x = Test()
x.truncate(0)
x.readinto(bytearray())
Does making the IBufferProtocol
overload virtual too make the error go away? I can't test it, am AFK for a while. If it does, then a possible explanation is the ambiguity between
write(Test self, object data)
write(BytesIO self, IBufferProtocol data)
(Actually, this is the only ambiguity I can come up with.)
The DLR prefers an overload with a derived type over a base type, so argument self
makes the first overload preferred, while argument data
favours the second. Hence the ambiguity.
Yep, making it virtual makes the error go away. I stepped through the code and it looks like the two candidates are exactly as you suggest:
{MethodCandidate(System.Numerics.BigInteger write(IronPython.Runtime.CodeContext, System.Object) on IronPython.NewTypes.IronPython.Modules.BytesIO_10$10)} {MethodCandidate(System.Numerics.BigInteger write(IronPython.Runtime.CodeContext, IronPython.Runtime.IBufferProtocol) on IronPython.Modules.PythonIOModule+BytesIO)}
Do you think the overload resolution should be able to figure out that the method was only overridden in ByteIO
? Or is this actually what's expected for virtual methods?
It looks like truncate
and readinto
(and probably other overloads) have this issue...
Possible solutions:
- Change the overload resolution.
- Make the overloads virtual.
- Make the overloads internal and let everything go through the public virtual overload.
Made the write overload virtual in https://github.com/IronLanguages/ironpython3/pull/1071 to get the test passing. Should revisit once we decide how to resolve this.
Do you think the overload resolution should be able to figure out that the method was only overridden in ByteIO? Or is this actually what's expected for virtual methods?
Frankly, I don't know. My intuition tells me that the overload resolution should be able to figure it out, but I don't know what the original intention of the DLR authors was, or whether there would be some unintended consequences. Current handling of the implicit self by the DLR in general seems to me somewhat hackish, so maybe it would be good to give it some extra thought and a little redesign at some point.
For now I think it is fortunate that a simple fix makes it work, seemingly without adverse effects.
Ran into this issue again (this time due to readinto
) in test_urllib
.
While working on #1456, I've found tests in test_methodbinder2.py
that test exactly this case: MethodBinder2Test.test_arg_Derived_Number
. From those tests it appears that the intention was to report such cases as ambiguous. The only non-ambiguous upcasting case is when the argument matches the base class parameter exactly.