Skip to content

Conversation

@Ryex
Copy link
Contributor

@Ryex Ryex commented Apr 4, 2019

a correct implementation of list.__delitem__ that supports int, slice, and negative indexing

ref #251

after some discussion on gitter I decided to implement a PySliceableSequenceMut trait and use it as the base for this __delitem__ method as part of this work. For now, it only has the del methods but the intention is to have set methods too to support correct __setitem__ implementation

TODO:

  • Test cases /snippits

Rachel Powers added 3 commits April 3, 2019 21:11
while it works as well as __setitem__ currently does this impliments no
support for slice indexing or negative indexing, adtionaly a bad index
panics insted of giving a vm type error!
@codecov-io
Copy link

codecov-io commented Apr 4, 2019

Codecov Report

Merging #783 into master will increase coverage by 0.07%.
The diff coverage is 56%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #783      +/-   ##
==========================================
+ Coverage   62.24%   62.32%   +0.07%     
==========================================
  Files          82       82              
  Lines       12944    13019      +75     
  Branches     2665     2682      +17     
==========================================
+ Hits         8057     8114      +57     
- Misses       3181     3188       +7     
- Partials     1706     1717      +11
Impacted Files Coverage Δ
vm/src/obj/objlist.rs 73.76% <100%> (+1.35%) ⬆️
vm/src/obj/objsequence.rs 54.44% <52.85%> (-0.59%) ⬇️
vm/src/compile.rs 75.58% <0%> (+0.2%) ⬆️
vm/src/vm.rs 72.23% <0%> (+0.35%) ⬆️
vm/src/obj/objstr.rs 67.88% <0%> (+0.36%) ⬆️
vm/src/frame.rs 68.69% <0%> (+0.79%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update c6e875f...e399401. Read the comment docs.

}
}

pub fn del_item<T: PySliceableSequenceMut>(
Copy link
Contributor

Choose a reason for hiding this comment

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

I see that this is modeled after get_item above - this is kind of the old way of doing things. Rather than checking here for each kind of sequence payload, I think it would be best for the list implementations of these to be defined directly in the PyListRef impl.

Copy link
Contributor

@adrian17 adrian17 Apr 4, 2019

Choose a reason for hiding this comment

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

the implementation probably could be reused for bytearray?

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this is complex enough to warrant the reuse, especially once it's refactored to use some of the newer conveniences. For example:

  • We could create a SequenceIndex type that implements TryFromObject that is accepted by list.__getitem__ and list.__setitem__ (similar to what I just did for range, but with an i32 instead of a PyintRef).
  • That impl would internally use i32::try_from_object which will raise "cannot fit" exception rather than doing it manually.
  • The checking for the sequence type isn't necessary in the concrete classes. It's basically an extra step that's only necessary because of the shared implementation. To me, this screams "wrong abstraction".

Copy link
Collaborator

Choose a reason for hiding this comment

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

It mostly checks the subscript type - the sequence is just used for the error message. (Is there a neat way to construct that error in the caller and avoid passing sequence through at all?)

Copy link
Contributor

Choose a reason for hiding this comment

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

@cthulahoops did you read my comment above? checking the subscript type seems like a strong candidate for a TryFromObject implementation.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not referring to the trait here (although yeah, tbh I'm not super fond of it and think an inner sequence type shared by list and tuple could be a good idea). I was just referring to get_item and this new del_item helper, because they seem a bit weird and unnecessary to me.

Copy link
Contributor

Choose a reason for hiding this comment

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

After thinking about this... Maybe it will be better to do it in the simplest way possible (YAGNI and all, even though we know it's going to be also needed for bytearray and more), and worry about reuse after we actually have two users of this.

Copy link
Contributor Author

@Ryex Ryex Apr 5, 2019

Choose a reason for hiding this comment

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

Ok, so perhaps I jumped the gun implementing this in one night without waiting for some more input.
So, correct me if I'm wrong but the consensus is to ditch the trait and implement it all on PyListRef?

additionally, per @OddCoincidence, suggestion, instead of using a PyObjectRef for the subscript: the implementation of a SequenceIndex enum using an i32 and a PySliceRef with a TryFromObject like the work done on #784 would make for far less explicit type checking and panic! branching.
should I go with this route?

I like these suggestions as in retrospect it seems much cleaner and more in the realm of 'simplest solution that works' that I ascribe to.

Copy link
Contributor Author

@Ryex Ryex Apr 5, 2019

Choose a reason for hiding this comment

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

pub enum SequenceIndex {
    Int(i32),
    Slice(PySliceRef)
}

impl TryFromObject for SequenceIndex {
    fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
        match_class!(obj,
            i @ PyInt => match i32::try_from_object(vm, i) {
                Ok(t)  => Ok(SequenceIndex::Int(t)),
                Err(e) => Err(e.into()),
            },
            s @ PySlice => Ok(SequenceIndex::Slice(s)),
            obj => Err(vm.new_type_error(format!(
                "sequence indices be integers or slices, not {}",
                obj.class(),
            )))
        )
    }
}

this is what was ment by internaly using i32::try_from_obj correct?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, that's the right idea! The first arm can be simplified with ?, i.e.:

i @ PyInt => Ok(SequenceIndex::Int(i32::try_from_object(vm, i.into_object())?),

Also note the addition of into_object: try_from_object takes a PyObjectRef so the downcasted i needs to be casted back to object (kinda weird I know but it can be cleaned up later).

Copy link
Collaborator

@cthulahoops cthulahoops left a comment

Choose a reason for hiding this comment

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

Welcome and thanks for the contribution!

This looks great aside from a small things.

Ryex added 3 commits April 5, 2019 03:59
- removal of del_item
- all del function impl on PyListRef
- stepped deletion now loops only over the range
  insted of the whole list
@cthulahoops
Copy link
Collaborator

cthulahoops commented Apr 6, 2019

Looks good!

(I wonder if some sort of iter_slice returning an iterator of indexes touched by a slice would avoid get and delete having to repeat all the slice logic. But let's leave that as possible further work.)

}
}

pub enum SequenceIndex {
Copy link
Contributor

Choose a reason for hiding this comment

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

could we reuse RangeIndex? We'll need a "int-or-slice" typy for most __getitem__-like functions.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

actually, by the time I completed the rework, I realized that, yes, it potentially could have just used RangeIndex I only need to convert to i32 once It came time to check bounds.

fn delindex(self, index: i32, vm: &VirtualMachine) -> PyResult {
        if let Some(pos_index) = self.get_pos(index) {
//...
}

could be

fn delindex(self, index: PyInt, vm: &VirtualMachine) -> PyResult {
        if let Some(pos_index) = self.get_pos(i32::try_from_object(vm, index.into_object())?) {
//...
}

however, I felt it would be best to leave it as is until directed to merge it. it seemed a little strange to be using something called RangeIndex.

@windelbouwman
Copy link
Contributor

@Ryex thank you for contributing to this project! Nice job in adding deletions!

@windelbouwman windelbouwman merged commit 1faa7fa into RustPython:master Apr 6, 2019
@Ryex Ryex deleted the ryex-impl-list.__delitem__ branch April 6, 2019 18:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants