-
Notifications
You must be signed in to change notification settings - Fork 406
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
Allow iterables of qualified class names in Stream.__getitem__
searches
#1359
Conversation
14a5d90
to
f423601
Compare
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.
Looks good. One question and some doc updates.
music21/stream/base.py
Outdated
k: t.Union[t.Type[ChangedM21ObjType], t.Collection[t.Type[ChangedM21ObjType]]] | ||
) -> iterator.RecursiveIterator[ChangedM21ObjType]: |
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.
does this properly type the output for mypy? (just want to be sure that that was checked)
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 I'm not quite following -- can you explain what you mean? (mypy
passes, and I didn't touch the return type)
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.
What I mean is something like, say you start typing:
for el in s[note.Note, chord.Chord]:
print(el.pitches)
print(el.recurse())
will the el.pitches
line start to auto-suggest .pitches when you've typed .pi... (like does it know that all possible members have .pitches in it), and will mypy choke on the el.recurse()
-- knowing that the union of note.Note and chord.Chord does not have .recurse()
defined on all members -- or even one member.
And if you do:
for el in s[note.Note, chord.Chord]:
if not isinstance(el, note.Note):
print(el.closedPosition())
and know that this is fine? I'm just wondering if the typing is inferred from the ChangedM21Type.
And most importantly, does this still raise a mypy error:
for el in s[note.Note]:
print(el.isMajorTriad())
while this passes?
for el in s[note.Note]:
print(el.pitch)
I'll just grab the PR and play with it. But I'm wondering if putting the two different types of things in the same @overload
might cause problems.
music21/stream/base.py
Outdated
@@ -486,7 +490,7 @@ def __getitem__(self, | |||
3.0 | |||
|
|||
|
|||
If a class is given then an iterator of elements | |||
If a class (or iterable of classes) is given, then an iterator of elements |
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.
better to demo these two things separately. If a class is given, then a RecursiveIterator of elements matching the class is returned, similar...
then later, "Multiple classes can be passed in separated by commas. Any element matching any of the classes will be returned." with the demo of that.
music21/stream/base.py
Outdated
for maybeType in ( | ||
k if common.isIterable(k) else [k] # type: ignore | ||
) | ||
): |
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.
not such a fan of this type of generator predicate. Clearer to make two separate elif statements, one for the single class and second one for the isIterable case. Students read this code and emulate 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.
Looks good -- I'll play with this and make the tiny quote/space change myself.
music21/stream/base.py
Outdated
k: t.Union[t.Type[ChangedM21ObjType], t.Collection[t.Type[ChangedM21ObjType]]] | ||
) -> iterator.RecursiveIterator[ChangedM21ObjType]: |
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.
What I mean is something like, say you start typing:
for el in s[note.Note, chord.Chord]:
print(el.pitches)
print(el.recurse())
will the el.pitches
line start to auto-suggest .pitches when you've typed .pi... (like does it know that all possible members have .pitches in it), and will mypy choke on the el.recurse()
-- knowing that the union of note.Note and chord.Chord does not have .recurse()
defined on all members -- or even one member.
And if you do:
for el in s[note.Note, chord.Chord]:
if not isinstance(el, note.Note):
print(el.closedPosition())
and know that this is fine? I'm just wondering if the typing is inferred from the ChangedM21Type.
And most importantly, does this still raise a mypy error:
for el in s[note.Note]:
print(el.isMajorTriad())
while this passes?
for el in s[note.Note]:
print(el.pitch)
I'll just grab the PR and play with it. But I'm wondering if putting the two different types of things in the same @overload
might cause problems.
music21/stream/base.py
Outdated
... if isinstance(note_or_rest, note.Note): | ||
... print(note_or_rest.name, end=' ') | ||
... else: | ||
... print("Rest", end = ' ') |
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.
quotes and spacing. (Sorry that the new linter doesn't do doctests.
No, in fact the new typing doesn't even know that el is a Music21Object. So the overload needs to be written separately. There was actually a reason for the stricter [RecurseType] needing to be a Music21Object -- that's how the operator knew that everything coming out was still a Music21Object. There's a way to make it still work. |
For typing I think we're going to need to leave in the stricter version and probably make .getElementsByClass() stricter too. People will need to do the .getElementsByClass('RepeatMark') way, or Adding general t.Type as one of the things that could filter meant that we were losing the amazing type-inference that was one of the main points of doing this refactor. Unfortunately Python does not yet have a |
.recurse().getElementsByClass()
, but unlike that long form, the short form didn't cope with iterables of qualified class names such as[note.Note, note.Rest]
:Inspired by mailing list message.
Also,
Music21Object
to match longhand syntax (Fixes Stream.__getitem__ is stricter about subclassingMusic21Object
thanClassFilter
#1360)