Skip to content
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

Fix numba.typed.List extend for singleton and empty iterable #5517

Merged
merged 5 commits into from
Apr 23, 2020
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
27 changes: 27 additions & 0 deletions numba/tests/test_typedlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,33 @@ def impl():
got = impl()
self.assertEqual(expected, got)

def test_extend_single_value_container(self):
@njit
def impl():
l = List()
l.extend((100,))
return l

expected = impl.py_func()
got = impl()
self.assertEqual(expected, got)

def test_extend_empty_unrefined(self):
# Extending an unrefined list with an empty iterable doesn't work in a
# jit compiled function as the list remains untyped.
l = List()
l.extend(tuple())
self.assertEqual(len(l), 0)
Comment on lines +744 to +748
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this behaviour a good idea? What about JIT transparency etc.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, if you put this in a JIT compiled function, you get:

Invalid use of Function(<function impl_extend at 0x7f5648715c10>) with argument(s) of type(s): (ListType[undefined], Tuple())
 * parameterized
In definition 0:
    TypingError: extend argument must be iterable

empty tuples are iterable, this is a bit confusing. I think it's arising because an empty tuple is typed as Tuple at present, which is not iterable for Numba's purposes.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think only UniTuple is iterable. I struggled with this one for some time and came to the conclusion that this is a corner case that only works outside of a jit context.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, even if tuple() would be something iterable, the function would not compile as the list can not be refined.

self.assertFalse(l._typed)

def test_extend_empty_refiend(self):
# Extending a refined list with an empty iterable doesn't work in a
# jit compiled function as the (empty) argument can't be typed
l = List((1,))
l.extend(tuple())
self.assertEqual(len(l), 1)
self.assertTrue(l._typed)


@njit
def cmp(a, b):
Expand Down
15 changes: 13 additions & 2 deletions numba/typed/typedlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -356,13 +356,24 @@ def pop(self, i=-1):

def extend(self, iterable):
if not self._typed:
iterable_length = len(iterable)
# Empty iterable, do nothing
if iterable_length == 0:
return self
# Need to get the first element of the iterable to initialise the
# type of the list. FIXME: this may be a problem if the iterable
# can not be sliced.
self._initialise_list(iterable[0])
self.append(iterable[0])
return _extend(self, iterable[1:])
return _extend(self, iterable)
# Iterable has more values to extend from
if iterable_length > 1:
return _extend(self, iterable[1:])
else:
return self
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this just be:

            self._initialise_list(iterable[0])
            return _extend(self, iterable)

i.e. init type, then extend, is there a need to append then slice from 1?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this can be significantly simplified.

elif len(iterable) == 0:
return self
else:
return _extend(self, iterable)

def remove(self, item):
return _remove(self, item)
Expand Down