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 ec6588e

Browse filesBrowse files
tomschrtlaferrierebittnerppktSébastien Celles
committed
Implement __getitem__ for #138
* 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>
1 parent d69e7c4 commit ec6588e
Copy full SHA for ec6588e

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
@@ -446,6 +446,42 @@ def __gt__(self, other):
446446
def __ge__(self, other):
447447
return self.compare(other) >= 0
448448

449+
def __getitem__(self, index):
450+
"""
451+
self.__getitem__(index) <==> self[index]
452+
453+
Implement getitem. If the part requested is undefined, or a part of the
454+
range requested is undefined, it will throw an index error.
455+
Negative indices are not supported
456+
457+
:param Union[int, slice] index: a positive integer indicating the
458+
offset or a :func:`slice` object
459+
:raises: IndexError, if index is beyond the range or a part is None
460+
:return: the requested part of the version at position index
461+
462+
>>> ver = semver.VersionInfo.parse("3.4.5")
463+
>>> ver[0], ver[1], ver[2]
464+
(3, 4, 5)
465+
"""
466+
if isinstance(index, int):
467+
index = slice(index, index + 1)
468+
469+
if (
470+
isinstance(index, slice)
471+
and (index.start is None or index.start < 0)
472+
and (index.stop is None or index.stop < 0)
473+
):
474+
raise IndexError("Version index cannot be negative")
475+
476+
# Could raise IndexError:
477+
part = tuple(filter(None, self.to_tuple()[index]))
478+
479+
if len(part) == 1:
480+
part = part[0]
481+
if not part:
482+
raise IndexError("Version part undefined")
483+
return part
484+
449485
def __repr__(self):
450486
s = ", ".join("%s=%r" % (key, val) for key, val in self.to_dict().items())
451487
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.