-
Notifications
You must be signed in to change notification settings - Fork 96
Add __getitem__
method to allow easy access to version parts
#138
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
Conversation
Thanks @tlaferriere for this PR |
I'll get on it. |
@scls19fr Should I add test vectors for edge cases or are these ones satisfactory? |
The implementation is certainly fine. I'm not super-convinced by the above use case, though. While it's a fact that Enums start at 1 for a reason I'd prefer to see a more intuitive syntax (i.e. something that doesn't need to do index calculations, namely The doctests of parse_version_info may also include access with constants, and we may provide the constants directly, e.g. >>> import semver
>>> version_info = semver.parse_version_info("3.4.5-pre.2+build.4")
>>> version_info.major == version_info[semver.MAJOR] == 3
True
>>> version_info.minor == version_info[semver.MINOR] == 4
True
...
|
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.
@tlaferriere - overall I'm happy with these changes - we are already supporting iteration over VersionInfo instance so, in my opinion, it's a quite nice feature to have an ability to have a support for indexing.
I have only some small remarks regarding your PR.
@bittner I like your idea! 👍 😃 |
This PR have a conflicting file (sorry I should have managed this one before but was and still is quite busy). |
# Conflicts: # semver.py
Please fix this conflict to have a chance to have this feature include in latest release! |
Excuse the delay, I am resolving the conflicts and finishing up tests. |
…ility function for check the validity of the index.
So I've decided to disallow negative indexing as it would require going against the nature of a tuple to make behave like |
Also I've decided not to add the |
…w useless _validate_index.
Thanks @tlaferriere for this first contribution here. Could you update doc to show this new feature? Could you also update CHANGELOG? |
You can also add your name to https://github.com/k-bx/python-semver/blob/master/CONTRIBUTORS |
semver.py
Outdated
@@ -168,6 +168,12 @@ def __iter__(self): | ||
for v in self._astuple(): | ||
yield v | ||
|
||
def __getitem__(self, index): | ||
"""Implement getitem. This automatically strips empty parts of the | ||
version from the iterable from which the index is taken.""" |
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.
Thanks @tlaferriere, could you change the docstring:
version from the iterable from which the index is taken.""" | |
version from the iterable from which the index is taken. | |
:param int index: a positive or negative integer indicating | |
the offset | |
:raises: IndexError, if index is beyond the range | |
""" |
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.
@tomschr according to current implementation, index can be negative.
@tlaferriere I think there is a subtle bug in your implementation:
def test_example():
v = VersionInfo.parse('1.2.3+build0')
> assert v[:] == (1, 2, 3, None, 'build0')
E AssertionError: assert (1, 2, 3, 'build0') == (1, 2, 3, None, 'build0')
E At index 3 diff: 'build0' != None
E Right contains more items, first extra item: 'build0'
E Use -v to get the full diff
i.e. if "prerelease" part is missing and "metadata" is present, after using getitem , "metadata" appears at index for "prerelease"
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.
according to current implementation, index can be negative.
You are right, I've corrected the GH suggestion. Thanks! 👍
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.
After much deliberation with myself and an increasingly complex function, I have decided to rewrite it (multiple times). I believe I have made this implementation as simple as possible. Turns out the subtle bug mentioned by @ppkt was a symptom of a few bad design decisions on my part. Now it doesn't support negative indices.
Co-Authored-By: Tom Schraitle <tomschr@users.noreply.github.com>
Conflicting files need to be solved here also. CI is failing and should be fixed also. |
� Conflicts: � test_semver.py
:param Union[int, slice] index: a positive integer indicating the | ||
offset or a slice | ||
:raises: IndexError, if index is beyond the range or a part is None | ||
""" |
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 docstring has an unusual indentation. According to PEP 257 indentation may look like:
def foo():
"""First line of comment
Second line of comment. The longer description.
"""
(what I like less but seems to be favored by Guido & friends), or:
def foo():
"""
First line of comment
Second line of comment. The longer description.
"""
Same below, in the method following next.
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.
Good point, but then the other docstrings needs to be adapted as well. Maybe we could do that in a combined issue later?
else: | ||
slice_is_full = True | ||
part = self._astuple()[index] | ||
for i in part: |
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 is i
here? Pylint would be complaining (if you ran it). Can you use a self-explanatory name?
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.
One idea could be to replace i
with item
.
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 guess I could use subpart
instead.
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.
See also the more recent comments below. 😉
raise negative_error | ||
else: | ||
part = self._astuple()[index] | ||
if part is None: |
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.
if not part:
... is probably more pythonic. Or is there a reason for an explicit None
?
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 is in several places in the code, by the way, and could be made pythonic throughout the code.
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.
Just a small comment. 😉
:param Union[int, slice] index: a positive integer indicating the | ||
offset or a slice | ||
:raises: IndexError, if index is beyond the range or a part is None | ||
""" |
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.
Good point, but then the other docstrings needs to be adapted as well. Maybe we could do that in a combined issue later?
@tlaferriere Found another issue. You could simplify the code a bit and omit the def __getitem__(self, index):
"""
Implement getitem. If the part requested is undefined, or a part of the
range requested is undefined, it will throw an index error.
Negative indices are not supported
:param Union[int, slice] index: a positive integer indicating the
offset or a slice
:raises: IndexError, if index is beyond the range or a part is None
"""
undefined_error = IndexError("Version part undefined")
negative_error = IndexError("Version index cannot be negative")
if isinstance(index, slice):
if (False if index.start is None else index.start < 0) or \
(False if index.stop is None else index.stop < 0):
raise negative_error
slice_is_full = True
part = self._astuple()[index]
for item in part:
if item is None:
slice_is_full = False
elif not slice_is_full:
raise undefined_error
part = tuple(filter(None, part))
else:
if index < 0:
raise negative_error
part = self._astuple()[index]
if part is None:
raise undefined_error
return part |
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.
See #138 (comment)
@tlaferriere def __getitem__(self, index):
"""Implement getitem. If the part requested is undefined, or a part of the
range requested is undefined, it will throw an index error.
Negative indices are not supported
:param Union[int, slice] index: a positive integer indicating the
offset or a slice
:raises: IndexError, if index is beyond the range or a part is None
:return: the requested part of the version at position index
"""
if isinstance(index, int):
index = slice(index, index+1)
if isinstance(index, slice) and \
(index.start is None or index.start < 0) and \
(index.stop is None or index.stop < 0):
raise IndexError("Version index cannot be negative")
# Could raise IndexError:
part = tuple(filter(None, self._astuple()[index]))
if len(part) == 1:
part = part[0]
if not part:
raise IndexError("Version part undefined")
return part Tests were successful. 🎉 |
@tlaferriere Thomas, what's your opinion on #138 (comment)? Could we finish this PR to publish it for the 2.9.1 release or do you need more time? 😉 If you don't have time yet due to the holiday season, I could finish it for you if you like. Let me know what you prefer. 😉 |
@tomschr with my finals ending soon, I figured I would have more time to finish it up and get it ready for the release. I like the snippet, I haven't had time to inspect it, but it sure seems cleaner. I'll get some work in the next few days and hopefully it will go well enough for it to be ready. |
Take your time. If you need any help, let us know. 😊 |
@tlaferriere Thomas, just a quick question: if you are too busy at the moment (which is completely fine!), should I finish the issue for you or would you prefer to do it yourself? Of course I will add you as co-author. 😉 |
* Add __getitem__ to VersionInfo class * Add test cases * Add user documentation * Extend CHANGELOG Co-authored-by: Thomas Laferriere <tlaferriere@users.noreply.github.com> Co-authored-by: Peter Bittner <bittner@users.noreply.github.com> Co-authored-by: Karol Werner <ppkt@users.noreply.github.com> Co-authored-by: Sébastien Celles <scls19fr@users.noreply.github.com>
I've created this PR anew in #243, so closing this. |
This is a natural extension of the work done in #124 (and parents) and makes it easy to access version parts using their index.
Use case:
Using an
Enum
to access the version part: