-
Notifications
You must be signed in to change notification settings - Fork 19
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
Adding tests #34
Adding tests #34
Conversation
tests/test_typechecker.py
Outdated
pytypes.resolve_fw_decl(Data) | ||
pytypes.resolve_fw_decl(Container) | ||
|
||
self.assertFalse(pytypes.is_subtype(list, Container)) |
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 think this should raise an exception.
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'd like to pretend that this succeeds because of recursion proof handling in type_util._issubclass_Union
/type_util._issubclass_Union_rec
. However I tried to out-comment the recursion-proof part and still get the same result. While this behavior is puzzling I would not investigate further unless we find a particular failure.
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.
So you are saying you disable recursion checks you added and this test still succeeds?
I copied it from here and at that time it failed for sure: #22 (comment)
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.
Oh right, the exception is what you actually suggested. Do you find it important to uncover recursive type declarations this way?
I think it would be possible to let type_util._issubclass_Union
throw an exception in such a case. I think I'd prefer to have such a behavior optional. Will think about it...
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 do not have strong opinion here. I just see it as an invalid type. So it would simplify debugging. But maybe this type is not invalid and it is true that isinstance should return False, if we would be able to resolve the recursion somehow. So I do not really mind either way. It just be that it will surprise some.
Awesome! Thanks a lot for writing this commit.
|
Updated, but tests are now failing. I added an assertion I missed but it does not return an expected value. It is a bug or a buggy test? |
tests/test_typechecker.py
Outdated
|
||
pytypes.is_subtype(typing.List[float], Container) | ||
self.assertTrue(pytypes.is_subtype(typing.List[float], Wrapper)) |
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 where the test fails. Before the change, the result was not checked to be true, it succeeded whenever no exception was thrown. Actually List[float]
is no subtype of Wrapper
because of invariance of List
's type parameter. Either construct Wrapper differently or change expected outcome of this test. E.g.:
Wrapper = typing.Union[
typing.Sequence['Data'],
]
Disclaimer: I din't check empirically. I hope my analysis is correct and this is the actual cause.
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.
How it is not? I can get there by simply rewriting:
Wrapper
-> Union[List[Data]]
-> List[Data]
-> List[Union[Wrapper, str, bytes, bool, float, int, dict]]
-> List[float]
So List[float]
should be a subtype of List[float]
, no?
>>> pytypes.is_subtype(typing.List[float], typing.List[float])
True
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.
List[Union[Wrapper, str, bytes, bool, float, int, dict]]
-> List[float]
is not valid, because Union[Wrapper, str, bytes, bool, float, int, dict]
is a less restrictive type than float
and List
is invariant. Imagine someone passes List[float]
to a function that expects List[Union[Wrapper, str, bytes, bool, float, int, dict]]
. Then the method might write integers or strings into the list.
What is maybe a bad logic in deep_type
is to conclude that something like [1.5, 2.3, 5.7]
is List[float]
. Just because is doesn't contain strings does not mean it is not intended to ever do so. This is a symptom of PEP 484 not being intended for runtime type checking. But python does not provide a way to annotate that a list object that currently contains only floats is also allowed to contain strings. (Except PEP 526 -- Syntax for Variable Annotations, but supporting PEP 526 is not a trivial enhanchement.) Some simpler solution would be needed.... Ideas?
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.
Okay, since this test is about another topic, please change it to Sequence
. We can follow up on List
invariance issues else where, e.g. in #21.
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.
Done.
c5aa628
to
0da6b11
Compare
tests/test_typechecker.py
Outdated
self.assertTrue(pytypes.is_subtype(typing.Sequence[float], Wrapper)) | ||
self.assertTrue(pytypes.is_subtype(int, Data)) | ||
self.assertTrue(pytypes.is_subtype(float, Data)) | ||
self.assertTrue(pytypes.is_subtype(Data, Wrapper)) |
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.
Now it fails here. Not sure why would this be false?
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.
Because currently Wrapper
is essentially equal to Sequence[Data]
, which is neither subtype nor supertype nor equal to plain Data
.
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.
But Data
is also Union[Wrapper]
, no?
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.
Yes, but that is not sufficient. Union[x, y]
is only subtype of z
if x
is subtype of z
and y
is subtype of z
. Since Data
can also be float
, str
, etc. it is in general not subtype of Wrapper
.
|
||
pytypes.resolve_fw_decl(Wrapper) | ||
|
||
self.assertTrue(pytypes.is_subtype(typing.Sequence[float], Wrapper)) |
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.
self.assertTrue(pytypes.is_subtype(typing.List[float], Wrapper)) should work as well, i.e. changing List to Sequence was not necessary here, only in declaration of Wrapper.
tests/test_typechecker.py
Outdated
self.assertTrue(pytypes.is_subtype(int, Data)) | ||
self.assertTrue(pytypes.is_subtype(float, Data)) | ||
self.assertFalse(pytypes.is_subtype(Data, Wrapper)) | ||
self.assertFalse(pytypes.is_subtype(Wrapper, Data)) |
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.
self.assertFalse(pytypes.is_subtype(Wrapper, Data))
is failing now. Unlike the line above, this is actually true. As participant of the Data-Union, Wrapper is indeed a subtype of it. As discussed earlier the converse is not true.
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.
Fixed. :-) I think I have no clue about typing anymore. ;-)
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.
Sorry to hear that. Hope it doesn't stop you from helping to improve pytypes ;)
Feel free to ask more detals if it helps. Here I think it boils down to union semantics, so let me point out the main rules again:
Union[x, y]
is subtype of z
iff x
is subtype of z
and y
is subtype of z
.
z
is subtype of Union[x, y]
iff z
is subtype of x
or z
is subtype of y
.
This directly corresponds to union operation from set theory if x, y, z are sets and subtype means subset. There is also the intersection correspondence in type theory, but PEP 484 does not provide an intersection type (was once discussed in an issue of typing, but then drpped because rare use cases did not outweight the difficulties in implementing it for static type checking/mypy)
Perfect! |
I added tests for #24 and changed the default.
I added tests for #22, #32, #33.