question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Idea: Use case for a VersionInfoIterator?

See original GitHub issue

Situation

For example, if we want to start from a certain version and get the next 3 versions we could do that:

v = VersionInfo.parse("1.2.3-rc.3")
for _ in range(3):
   print(v.next_version(part="prerelease"))

This would work, but maybe we could use the iterator protocol for that.

Proposed Solution

The solution is very rough, but that’s on purpose. It’s a minimal example to demonstrate the idea and not get distracted by other methods. So I know, this would need fine-tuning (add properties, protect from invalid datatypes etc.).

class VersionInfoIterator:
    """Iterator over VersionInfo objects"""
    def __init__(self, version, part, steps=10):
        self.version = version
        self.part = part
        self._steps = steps

    def __iter__(self):
        return self

    def __repr__(self):
        cls = type(self)
        return "{}(version={!r}, part={!r}, steps={})".format(cls.__name__,
                self.version, self.part, self._steps)

    def __next__(self):
        if not self.steps:
            raise StopIteration
        self._steps -= 1
        self.version = self.version.next_version(self.part)
        return self.version

An implementation could look like this:

from semver import VersionInfo, VersionInfoIterator
>>> v = VersionInfo.parse("1.2.3-rc.3")
>>> vit = VersionInfoIterator(v, "prerelease", steps=3)
>>> next(vit)                                               
VersionInfo(major=1, minor=2, patch=3, prerelease='rc.4', build=None)
>>> next(vit)                                                                                                                              
VersionInfo(major=1, minor=2, patch=3, prerelease='rc.5', build=None)
>>> vit.part = "minor"
>>> next(vit)    
VersionInfo(major=1, minor=3, patch=0, prerelease=None, build=None)
>>> next(vit)
Traceback (most recent call last)
...
StopIteration

# or

>>> vit = VersionInfoIterator(v, "prerelease", steps=3)
>>> list(vit)
[VersionInfo(major=1, minor=2, patch=3, prerelease='rc.4', build=None),
 VersionInfo(major=1, minor=2, patch=3, prerelease='rc.5', build=None),
 VersionInfo(major=1, minor=2, patch=3, prerelease='rc.6', build=None)]

Questions

  • Would such an iterator be useful?
  • Is it necessary or is first example above enough when iterating?
  • If you think it could be useful, would you change something in __init__?

Actually, I’m not sure myself if this is something good or completely unnecessary. I just had this idea when working on issue #222. 😉 Also I thought, it would be helpful to document it, regardless if this will be accepted or not.

@gsakkis, @scls19fr What do you think? I would like to hear your opinion. 😉

Issue Analytics

  • State:open
  • Created 4 years ago
  • Comments:8 (8 by maintainers)

github_iconTop GitHub Comments

2reactions
gsakkiscommented, Feb 29, 2020

I kinda like the general idea and had also thought of something similar. Two comments for the specific API:

  • steps is unnecessary, this can (and should in my opinion) be an infinite iterator. The caller can always use itertools.islice or others to limit/filter the iterable.
  • Bumping to a different part than the initial one by setting an attribute is not ideal because (a) it is a 2-step process (vit.part = "minor"; next(vit)) and (b) it is sticky; all subsequent next calls will use the new part, although in practice I’d guess it would be more common to bump to minor/major once but continue with the original part for subsequent next versions.

Based on these I came up with a simple coroutine approach, where you can send(part) for a single next version but preserve the original:

def iter_versions(version, part, prerelease_token="rc"):
    current_part = part
    while True:
        version = next_version(version, current_part, prerelease_token)
        current_part = (yield version) or part

Usage:

import itertools as it

def test_iter_versions():
    iv = iter_versions("1.2.3-rc.3", "prerelease")
    assert list(it.islice(iv, 3)) == ["1.2.3-rc.4", "1.2.3-rc.5", "1.2.3-rc.6"]
    assert next(iv) == "1.2.3-rc.7"
    assert next(iv) == "1.2.3-rc.8"
    assert iv.send("patch") == "1.2.3"
    assert next(iv) == "1.2.4-rc.1"
    assert iv.send("minor") == "1.3.0"
    assert next(iv) == "1.3.1-rc.1"
    assert iv.send("major") == "2.0.0"
    assert next(iv) == "2.0.1-rc.1"
1reaction
gsakkiscommented, Apr 13, 2020

Hmn, I’m wondering about the parts argument. Shouldn’t it be *parts?

Not if we want to support arbitrary iterables without expanding them into tuples first.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Idea: Extend Iterators to break on condition
This idea is based on the need to break an iteration loop when a specific condition is met. As an example, you have...
Read more >
Iterator Pattern
Iterator Pattern is a relatively simple and frequently used design pattern. There are a lot of data structures/collections available in every ...
Read more >
Are there use cases where you would want to implement ...
Are there use cases where you would want to implement an iterator yourself? Here are some reasons I can think of for implementing...
Read more >
itertools — Functions creating iterators for efficient looping
This module implements a number of iterator building blocks inspired by constructs from APL, Haskell, and SML. Each has been recast in a...
Read more >
Idea: Fallible iterator mapping with `try_map`
I've come across a use case where I'm looking for a fallible version of the Map iterator, much like the version specified for...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found