Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 2ef2eea

Browse filesBrowse files
authored
Merge pull request #243 from tomschr/feature/138-add-__getitem__
Implement __getitem__ for #138
2 parents 8d4336e + ec6588e commit 2ef2eea
Copy full SHA for 2ef2eea

File tree

4 files changed

+162
-2
lines changed
Filter options

4 files changed

+162
-2
lines changed

‎CHANGELOG.rst

Copy file name to clipboardExpand all lines: CHANGELOG.rst
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ Version 2.10.0 (WIP)
1515
Features
1616
--------
1717

18+
* :pr:`138`: Added ``__getitem__`` magic method to ``semver.VersionInfo`` class.
19+
Allows to access a version like ``version[1]``.
1820
* :pr:`235`: Improved documentation and shift focus on ``semver.VersionInfo`` instead of advertising
1921
the old and deprecated module-level functions.
2022

‎docs/usage.rst

Copy file name to clipboardExpand all lines: docs/usage.rst
+53-2Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,10 @@ classmethod :func:`semver.VersionInfo.isvalid`:
141141
False
142142
143143
144-
Accessing Parts of a Version
145-
----------------------------
144+
.. _sec.properties.parts:
145+
146+
Accessing Parts of a Version Through Names
147+
------------------------------------------
146148

147149
The :class:`semver.VersionInfo` contains attributes to access the different
148150
parts of a version:
@@ -184,6 +186,55 @@ In case you need the different parts of a version stepwise, iterate over the :cl
184186
[3, 4, 5, 'pre.2', 'build.4']
185187

186188

189+
.. _sec.getitem.parts:
190+
191+
Accessing Parts Through Index Numbers
192+
-------------------------------------
193+
194+
.. versionadded:: 2.10.0
195+
196+
Another way to access parts of a version is to use an index notation. The underlying
197+
:class:`VersionInfo <semver.VersionInfo>` object allows to access its data through
198+
the magic method :func:`__getitem__ <semver.VersionInfo.__getitem__>`.
199+
200+
For example, the ``major`` part can be accessed by index number 0 (zero).
201+
Likewise the other parts:
202+
203+
.. code-block:: python
204+
205+
>>> ver = semver.VersionInfo.parse("10.3.2-pre.5+build.10")
206+
>>> ver[0], ver[1], ver[2], ver[3], ver[4]
207+
(10, 3, 2, 'pre.5', 'build.10')
208+
209+
If you need more than one part at the same time, use the slice notation:
210+
211+
.. code-block:: python
212+
213+
>>> ver[0:3]
214+
(10, 3, 2)
215+
216+
Or, as an alternative, you can pass a :func:`slice` object:
217+
218+
.. code-block:: python
219+
220+
>>> sl = slice(0,3)
221+
>>> ver[sl]
222+
(10, 3, 2)
223+
224+
Negative numbers or undefined parts raise an :class:`IndexError` exception:
225+
226+
.. code-block:: python
227+
228+
>>> ver = semver.VersionInfo.parse("10.3.2")
229+
>>> ver[3]
230+
Traceback (most recent call last):
231+
...
232+
IndexError: Version part undefined
233+
>>> ver[-2]
234+
Traceback (most recent call last):
235+
...
236+
IndexError: Version index cannot be negative
237+
187238
.. _sec.replace.parts:
188239

189240
Replacing Parts of a Version

‎semver.py

Copy file name to clipboardExpand all lines: semver.py
+36Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,42 @@ def __gt__(self, other):
493493
def __ge__(self, other):
494494
return self.compare(other) >= 0
495495

496+
def __getitem__(self, index):
497+
"""
498+
self.__getitem__(index) <==> self[index]
499+
500+
Implement getitem. If the part requested is undefined, or a part of the
501+
range requested is undefined, it will throw an index error.
502+
Negative indices are not supported
503+
504+
:param Union[int, slice] index: a positive integer indicating the
505+
offset or a :func:`slice` object
506+
:raises: IndexError, if index is beyond the range or a part is None
507+
:return: the requested part of the version at position index
508+
509+
>>> ver = semver.VersionInfo.parse("3.4.5")
510+
>>> ver[0], ver[1], ver[2]
511+
(3, 4, 5)
512+
"""
513+
if isinstance(index, int):
514+
index = slice(index, index + 1)
515+
516+
if (
517+
isinstance(index, slice)
518+
and (index.start is None or index.start < 0)
519+
and (index.stop is None or index.stop < 0)
520+
):
521+
raise IndexError("Version index cannot be negative")
522+
523+
# Could raise IndexError:
524+
part = tuple(filter(None, self.to_tuple()[index]))
525+
526+
if len(part) == 1:
527+
part = part[0]
528+
if not part:
529+
raise IndexError("Version part undefined")
530+
return part
531+
496532
def __repr__(self):
497533
s = ", ".join("%s=%r" % (key, val) for key, val in self.to_dict().items())
498534
return "%s(%s)" % (type(self).__name__, s)

‎test_semver.py

Copy file name to clipboardExpand all lines: test_semver.py
+71Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,77 @@ def test_should_be_able_to_use_integers_as_prerelease_build():
695695
assert VersionInfo(1, 2, 3, 4, 5) == VersionInfo(1, 2, 3, "4", "5")
696696

697697

698+
@pytest.mark.parametrize(
699+
"version, index, expected",
700+
[
701+
# Simple positive indices
702+
("1.2.3-rc.0+build.0", 0, 1),
703+
("1.2.3-rc.0+build.0", 1, 2),
704+
("1.2.3-rc.0+build.0", 2, 3),
705+
("1.2.3-rc.0+build.0", 3, "rc.0"),
706+
("1.2.3-rc.0+build.0", 4, "build.0"),
707+
("1.2.3-rc.0", 0, 1),
708+
("1.2.3-rc.0", 1, 2),
709+
("1.2.3-rc.0", 2, 3),
710+
("1.2.3-rc.0", 3, "rc.0"),
711+
("1.2.3", 0, 1),
712+
("1.2.3", 1, 2),
713+
("1.2.3", 2, 3),
714+
],
715+
)
716+
def test_version_info_should_be_accessed_with_index(version, index, expected):
717+
version_info = VersionInfo.parse(version)
718+
assert version_info[index] == expected
719+
720+
721+
@pytest.mark.parametrize(
722+
"version, slice_object, expected",
723+
[
724+
# Slice indices
725+
("1.2.3-rc.0+build.0", slice(0, 5), (1, 2, 3, "rc.0", "build.0")),
726+
("1.2.3-rc.0+build.0", slice(0, 4), (1, 2, 3, "rc.0")),
727+
("1.2.3-rc.0+build.0", slice(0, 3), (1, 2, 3)),
728+
("1.2.3-rc.0+build.0", slice(0, 2), (1, 2)),
729+
("1.2.3-rc.0+build.0", slice(3, 5), ("rc.0", "build.0")),
730+
("1.2.3-rc.0", slice(0, 4), (1, 2, 3, "rc.0")),
731+
("1.2.3-rc.0", slice(0, 3), (1, 2, 3)),
732+
("1.2.3-rc.0", slice(0, 2), (1, 2)),
733+
("1.2.3", slice(0, 10), (1, 2, 3)),
734+
("1.2.3", slice(0, 3), (1, 2, 3)),
735+
("1.2.3", slice(0, 2), (1, 2)),
736+
# Special cases
737+
("1.2.3-rc.0+build.0", slice(3), (1, 2, 3)),
738+
("1.2.3-rc.0+build.0", slice(0, 5, 2), (1, 3, "build.0")),
739+
("1.2.3-rc.0+build.0", slice(None, 5, 2), (1, 3, "build.0")),
740+
("1.2.3-rc.0+build.0", slice(5, 0, -2), ("build.0", 3)),
741+
],
742+
)
743+
def test_version_info_should_be_accessed_with_slice_object(
744+
version, slice_object, expected
745+
):
746+
version_info = VersionInfo.parse(version)
747+
assert version_info[slice_object] == expected
748+
749+
750+
@pytest.mark.parametrize(
751+
"version, index",
752+
[
753+
("1.2.3-rc.0+build.0", -1),
754+
("1.2.3-rc.0", -1),
755+
("1.2.3-rc.0", 4),
756+
("1.2.3", -1),
757+
("1.2.3", 3),
758+
("1.2.3", 4),
759+
("1.2.3", 10),
760+
("1.2.3", slice(-3)),
761+
],
762+
)
763+
def test_version_info_should_throw_index_error(version, index):
764+
version_info = VersionInfo.parse(version)
765+
with pytest.raises(IndexError):
766+
version_info[index]
767+
768+
698769
@pytest.mark.parametrize(
699770
"cli,expected",
700771
[

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.