Skip to content

Implement __getitem__ for #138 #243

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

Merged
merged 1 commit into from
May 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ Version 2.10.0 (WIP)
Features
--------

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

Expand Down
55 changes: 53 additions & 2 deletions docs/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,10 @@ classmethod :func:`semver.VersionInfo.isvalid`:
False


Accessing Parts of a Version
----------------------------
.. _sec.properties.parts:

Accessing Parts of a Version Through Names
------------------------------------------

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


.. _sec.getitem.parts:

Accessing Parts Through Index Numbers
-------------------------------------

.. versionadded:: 2.10.0

Another way to access parts of a version is to use an index notation. The underlying
:class:`VersionInfo <semver.VersionInfo>` object allows to access its data through
the magic method :func:`__getitem__ <semver.VersionInfo.__getitem__>`.

For example, the ``major`` part can be accessed by index number 0 (zero).
Likewise the other parts:

.. code-block:: python

>>> ver = semver.VersionInfo.parse("10.3.2-pre.5+build.10")
>>> ver[0], ver[1], ver[2], ver[3], ver[4]
(10, 3, 2, 'pre.5', 'build.10')

If you need more than one part at the same time, use the slice notation:

.. code-block:: python

>>> ver[0:3]
(10, 3, 2)

Or, as an alternative, you can pass a :func:`slice` object:

.. code-block:: python

>>> sl = slice(0,3)
>>> ver[sl]
(10, 3, 2)

Negative numbers or undefined parts raise an :class:`IndexError` exception:

.. code-block:: python

>>> ver = semver.VersionInfo.parse("10.3.2")
>>> ver[3]
Traceback (most recent call last):
...
IndexError: Version part undefined
>>> ver[-2]
Traceback (most recent call last):
...
IndexError: Version index cannot be negative

.. _sec.replace.parts:

Replacing Parts of a Version
Expand Down
36 changes: 36 additions & 0 deletions semver.py
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,42 @@ def __gt__(self, other):
def __ge__(self, other):
return self.compare(other) >= 0

def __getitem__(self, index):
"""
self.__getitem__(index) <==> 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 :func:`slice` object
:raises: IndexError, if index is beyond the range or a part is None
:return: the requested part of the version at position index

>>> ver = semver.VersionInfo.parse("3.4.5")
>>> ver[0], ver[1], ver[2]
(3, 4, 5)
"""
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.to_tuple()[index]))

if len(part) == 1:
part = part[0]
if not part:
raise IndexError("Version part undefined")
return part

def __repr__(self):
s = ", ".join("%s=%r" % (key, val) for key, val in self.to_dict().items())
return "%s(%s)" % (type(self).__name__, s)
Expand Down
71 changes: 71 additions & 0 deletions test_semver.py
Original file line number Diff line number Diff line change
Expand Up @@ -695,6 +695,77 @@ def test_should_be_able_to_use_integers_as_prerelease_build():
assert VersionInfo(1, 2, 3, 4, 5) == VersionInfo(1, 2, 3, "4", "5")


@pytest.mark.parametrize(
"version, index, expected",
[
# Simple positive indices
("1.2.3-rc.0+build.0", 0, 1),
("1.2.3-rc.0+build.0", 1, 2),
("1.2.3-rc.0+build.0", 2, 3),
("1.2.3-rc.0+build.0", 3, "rc.0"),
("1.2.3-rc.0+build.0", 4, "build.0"),
("1.2.3-rc.0", 0, 1),
("1.2.3-rc.0", 1, 2),
("1.2.3-rc.0", 2, 3),
("1.2.3-rc.0", 3, "rc.0"),
("1.2.3", 0, 1),
("1.2.3", 1, 2),
("1.2.3", 2, 3),
],
)
def test_version_info_should_be_accessed_with_index(version, index, expected):
version_info = VersionInfo.parse(version)
assert version_info[index] == expected


@pytest.mark.parametrize(
"version, slice_object, expected",
[
# Slice indices
("1.2.3-rc.0+build.0", slice(0, 5), (1, 2, 3, "rc.0", "build.0")),
("1.2.3-rc.0+build.0", slice(0, 4), (1, 2, 3, "rc.0")),
("1.2.3-rc.0+build.0", slice(0, 3), (1, 2, 3)),
("1.2.3-rc.0+build.0", slice(0, 2), (1, 2)),
("1.2.3-rc.0+build.0", slice(3, 5), ("rc.0", "build.0")),
("1.2.3-rc.0", slice(0, 4), (1, 2, 3, "rc.0")),
("1.2.3-rc.0", slice(0, 3), (1, 2, 3)),
("1.2.3-rc.0", slice(0, 2), (1, 2)),
("1.2.3", slice(0, 10), (1, 2, 3)),
("1.2.3", slice(0, 3), (1, 2, 3)),
("1.2.3", slice(0, 2), (1, 2)),
# Special cases
("1.2.3-rc.0+build.0", slice(3), (1, 2, 3)),
("1.2.3-rc.0+build.0", slice(0, 5, 2), (1, 3, "build.0")),
("1.2.3-rc.0+build.0", slice(None, 5, 2), (1, 3, "build.0")),
("1.2.3-rc.0+build.0", slice(5, 0, -2), ("build.0", 3)),
],
)
def test_version_info_should_be_accessed_with_slice_object(
version, slice_object, expected
):
version_info = VersionInfo.parse(version)
assert version_info[slice_object] == expected


@pytest.mark.parametrize(
"version, index",
[
("1.2.3-rc.0+build.0", -1),
("1.2.3-rc.0", -1),
("1.2.3-rc.0", 4),
("1.2.3", -1),
("1.2.3", 3),
("1.2.3", 4),
("1.2.3", 10),
("1.2.3", slice(-3)),
],
)
def test_version_info_should_throw_index_error(version, index):
version_info = VersionInfo.parse(version)
with pytest.raises(IndexError):
version_info[index]


@pytest.mark.parametrize(
"cli,expected",
[
Expand Down
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy