Uh oh!
There was an error while loading. Please reload this page.
Add .keys and .values to bind_map - #3310
Conversation
bmerry
commented
Sep 27, 2021
CC @henryiii@rainwoodman since this is a follow-on to #3271 / #3293. |
bmerry
commented
Sep 27, 2021
I've realised that one limitation of the |
henryiii
commented
Sep 27, 2021
That sounds reasonable to me. You could do a cast and have one impl, or have two. |
henryiii
commented
Sep 27, 2021
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)? |
bmerry
commented
Sep 27, 2021
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). |
Uh oh!
There was an error while loading. Please reload this page.
henryiii
commented
Sep 28, 2021
But only code that uses this increases in size (which the tests do), so personally, I think it's probably fine. |
571bff8 to
a32db87CompareWere you going to handle arbitrary inputs to |
bmerry
commented
Sep 30, 2021
Thanks for reminding me. Done. |
henryiii
commented
Sep 30, 2021
Wouldn't this potentially be smaller in binary form and have simpler signatures if you just had a single function with a |
bmerry
commented
Sep 30, 2021
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 |
henryiii
commented
Sep 30, 2021
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. |
Skylion007
commented
Sep 30, 2021
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? |
rwgk
commented
Sep 30, 2021
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
9422f17Comparerwgk
commented
Sep 30, 2021
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 |
henryiii
commented
Oct 1, 2021
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.
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.
@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.
@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
.itemsis 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: