-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
Add .keys
and .values
to bind_map
#3310
Conversation
CC @henryiii @rainwoodman since this is a follow-on to #3271 / #3293. |
I've realised that one limitation of the |
That sounds reasonable to me. You could do a cast and have one impl, or have two. |
Wait, what increases in size? This should only affect code that uses std_bind to bind maps? Are you just looking at the test size increase (which will be larger due to more tests)? |
Yes, the shared library for the tests. There isn't actually any new code C++ code for tests (there is new code in the .py files to test the extra bindings). |
But only code that uses this increases in size (which the tests do), so personally, I think it's probably fine. |
571bff8
to
a32db87
Compare
Were you going to handle arbitrary inputs to |
Thanks for reminding me. Done. |
Wouldn't this potentially be smaller in binary form and have simpler signatures if you just had a single function with a |
I'll take a look when I can - unfortunately I have some other commitments for the next week. I went with overloading because I couldn't see a way to do it in one function without a |
Okay, let's go with this unless someone wants to try something else. It could later be optimized if that does end up helping. The goal is to release 2.8 on or before Python 3.10 (Monday), and this is the one feature that would be nice to get in, since it builds on the existing ones. This technically was started before our feature freeze on Monday. @rwgk or @Skylion007 what do you think? We should also get one more Google test run with this in before releasing this weekend / Monday. |
On one hand, it was started before our feature freeze. On the other hand, this new functionality could add breaking functionality, @rwgk What does Google Testing say about this PR? |
I support this PR and will run the Google testing asap (today, result late tonight or tomorrow morning). |
Both of these implement views (rather than just iterators), and `.items` is also upgraded to a view. In practical terms, this allows a view to be iterated multiple times and have its size taken, neither of which works with an iterator. The views implement `__len__`, `__iter__`, and the keys view implements `__contains__`. Testing membership also works in item and value views because Python falls back to iteration. This won't be optimal for item values since it's linear rather than O(log n) or O(1), but I didn't fancy trying to get all the corner cases to match Python behaviour (tuple of wrong types, wrong length tuple, not a tuple etc). Missing relative to Python dictionary views is `__reversed__` (only added to Python in 3.8). Implementing that could break code that binds custom map classes which don't provide `rbegin`/`rend` (at least without doing clever things with SFINAE), so I've not tried. The size increase on my system is 131072 bytes, which is rather large (5%) but also suspiciously round (2^17) and makes me suspect some quantisation effect.
Add extra overload of `__contains__` (for both the map itself and KeysView) which takes an arbitrary object and returns false.
To keep clang-tidy happy.
b03ce2a
to
9422f17
Compare
Internally Google has a special way of handling header files, which exposed this small oversight:
Interactive testing (before submitting for global testing) passes after removing 5 |
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.
Google tests look good. The code looks good to me, too. I support including this in the 2.8 release, but I think it's best to leave it up to @henryiii to merge.
I think it's fine to go in. Let's view master as 2.8 RC. Full version will come out Monday. |
Summary: `Map` data such as `shots` is exposed through view classes. We need to keep the map alive as long as some of its objects are alive in python. For this to happen, we need - make the views keep alive the map - make the shots keep alive the view A better solution will be to use pybind's own bindings for `unordered_map`. However, this is only complete in pybind 2.8 pybind/pybind11#3310 Differential Revision: D32401524 fbshipit-source-id: 3fc30e710a2151dbb5097de8fe685ddfa80df675
Summary: Pull Request resolved: #822 `Map` data such as `shots` is exposed through view classes. We need to keep the map alive as long as some of its objects are alive in python. For this to happen, we need - make the views keep alive the map - make the shots keep alive the view A better solution will be to use pybind's own bindings for `unordered_map`. However, this is only complete in pybind 2.8 pybind/pybind11#3310 Reviewed By: fabianschenk Differential Revision: D32401524 fbshipit-source-id: 126141a7f02abe27fd376ae63fc788e87565dc0e
class_<KeysView> keys_view( | ||
scope, ("KeysView[" + name + "]").c_str(), pybind11::module_local(local)); | ||
class_<ValuesView> values_view( | ||
scope, ("ValuesView[" + name + "]").c_str(), pybind11::module_local(local)); | ||
class_<ItemsView> items_view( | ||
scope, ("ItemsView[" + name + "]").c_str(), pybind11::module_local(local)); |
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 naming seems to produce "erroneous" names for stub generation and subsequent parsing by Pylance or mypy, etc... Also sphinx autodoc has issues parsing it:
reading sources... [ 40%] references/python
WARNING: invalid signature for autoclass ('depthai::BoardConfig.ItemsView[GPIOMap]')
Thoughts on renaming to *View_{name}
, with an underscore? Seems to work well afterwards.
Example
Before:
class GPIOMap:
def __init__(self) -> None: ...
def items(self) -> BoardConfig.ItemsView[GPIOMap]: ...
def keys(self) -> BoardConfig.KeysView[GPIOMap]: ...
def values(self) -> BoardConfig.ValuesView[GPIOMap]: ...
def __bool__(self) -> bool: ...
@overload
def __contains__(self, arg0: int) -> bool: ...
@overload
def __contains__(self, arg0: object) -> bool: ...
def __delitem__(self, arg0: int) -> None: ...
def __getitem__(self, arg0: int) -> BoardConfig.GPIO: ...
def __iter__(self) -> Iterator: ...
def __len__(self) -> int: ...
def __setitem__(self, arg0: int, arg1: BoardConfig.GPIO) -> None: ...
class ItemsView[GPIOMap]:
def __init__(self, *args, **kwargs) -> None: ...
def __iter__(self) -> Iterator: ...
def __len__(self) -> int: ...
...
After:
class GPIOMap:
def __init__(self) -> None: ...
def items(self) -> BoardConfig.ItemsView_GPIOMap: ...
def keys(self) -> BoardConfig.KeysView_GPIOMap: ...
def values(self) -> BoardConfig.ValuesView_GPIOMap: ...
def __bool__(self) -> bool: ...
@overload
def __contains__(self, arg0: int) -> bool: ...
@overload
def __contains__(self, arg0: object) -> bool: ...
def __delitem__(self, arg0: int) -> None: ...
def __getitem__(self, arg0: int) -> BoardConfig.GPIO: ...
def __iter__(self) -> Iterator: ...
def __len__(self) -> int: ...
def __setitem__(self, arg0: int, arg1: BoardConfig.GPIO) -> None: ...
class ItemsView_GPIOMap:
def __init__(self, *args, **kwargs) -> None: ...
def __iter__(self) -> Iterator: ...
def __len__(self) -> int: ...
...
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.
@themarpe sounds good. Please add a PR.
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.
@themarpe Actually, hold on. Why is it having any issues? It should be generating a typing.KeysView . Is there some other reason here why it is struggling to parse it correctly?
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.
@Skylion007 Thanks - will do so soon.
EDIT: Opened the PR
I think its mainly from the *View[name]
naming. I think the square brackets gets at it. Works okay for mypy & Sphinx when renamed to something different
Description
Both of these implement views (rather than just iterators), and
.items
is also upgraded to a view. In practical terms, this allows a view to be
iterated multiple times and have its size taken, neither of which works
with an iterator.
The views implement
__len__
,__iter__
, and the keys view implements__contains__
. Testing membership also works in item and value viewsbecause Python falls back to iteration. This won't be optimal
for item values since it's linear rather than O(log n) or O(1), but I
didn't fancy trying to get all the corner cases to match Python
behaviour (tuple of wrong types, wrong length tuple, not a tuple etc).
Missing relative to Python dictionary views is
__reversed__
(onlyadded to Python in 3.8). Implementing that could break code that binds
custom map classes which don't provide
rbegin
/rend
(at least withoutdoing clever things with SFINAE), so I've not tried.
The size increase on my system is 131072 bytes, which is rather large
(5%) but also suspiciously round (2^17) and makes me suspect some
quantisation effect.
Suggested changelog entry: