-
Notifications
You must be signed in to change notification settings - Fork 134
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
Add support for version compare and for checking for version part argument #20
base: master
Are you sure you want to change the base?
Conversation
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 just an superficial review. I didn't look into all the details yet.
if not isinstance(other, Version): | ||
return False | ||
try: | ||
return all(v == other[k] for k, v in self.items()) |
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.
Doesn't this ignore the case there len(self.items()) < len(other.items())
?
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.
It does, we could do a len compare first to make sure both have the same number of parts. I will add this.
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.
On a second thought, I think equality should be strict, that is: 1.4 != 1.4.0
, by assuming they are the same we ignore the version pattern defined by parse and serialize. If both versions where generated by bumpversion then this highlights that the user has an error in the part configuration.
bumpversion/__init__.py
Outdated
for vals in ((v, other[k]) for k, v in self.items()): | ||
if vals[0] == vals[1]: | ||
continue | ||
if method(vals[0], vals[1]): |
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.
Can be return method(vals[0], vals[1])
but then I also prefer to rewrite it a bit:
if vals[0] != vals[1]:
return method(vals[0], vals[1])
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.
yes, cant really remember why did I do a if to return true/false.
bumpversion/version_part.py
Outdated
return hash(self.value) | ||
|
||
def _compare(self, other, method): | ||
if self.config.function_cls is not other.config.function_cls: |
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.
Odd indenting here
Sorry for the delay on this. Have pushed the requested changes, except for the equals (see my comments). |
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.
It took a while to get back to this. Some inline comments.
bumpversion/__init__.py
Outdated
@@ -336,7 +342,59 @@ def __iter__(self): | |||
return iter(self._values) | |||
|
|||
def __repr__(self): | |||
return '<bumpversion.Version:{}>'.format(keyvaluestring(self._values)) | |||
by_part = ", ".join("{}={}".format(k, v) for k, v in self.items()) | |||
return '<bumpversion.Version:{}>'.format(by_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.
Probably cleaner to use self.__class__
to show the class name rather than hardcoding.
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.
Makes sense. Do notice that this is how it was done in the past, I just added the by_part line ;)
bumpversion/__init__.py
Outdated
return not self.__eq__(other) | ||
|
||
def __le__(self, other): | ||
return self._compare(other, lambda s, o: o > s, False) # Note the change of order in operands |
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.
You can also use operator.lt
instead of the lambda. There's also operator.gt
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.
Agreed
def __hash__(self): | ||
return hash(tuple((k, v) for k, v in self.items())) | ||
|
||
def _compare(self, other, method, strict=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.
This function is really hard to understand at first sight. The strict
parameter should be named something clearer, perhaps allow_equal
?
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.
Indeed, I have changed the logic and improved the docs so the intent is more clear
bumpversion/__init__.py
Outdated
def keyvaluestring(d): | ||
return ", ".join("{}={}".format(k, v) for k, v in sorted(d.items())) | ||
|
||
class Version(object): | ||
|
||
def __init__(self, values, original=None): | ||
def __init__(self, values, config, original=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.
I'm not a fan of the config here. It doesn't feel very logical that a version has a config, especially if it's only used for order.
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.
Agreed, will only use the order
bumpversion/__init__.py
Outdated
self._values = dict(values) | ||
self.config = config |
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 IMHO an internal property so should be named self._config
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.
Config is now the order and it has to be public because when comparing we first compare the orders cause if they are different the versions can not be comapred
bumpversion/version_part.py
Outdated
def __eq__(self, other): | ||
return self.value == other.value | ||
return self._compare(other, lambda s, o: s == o) |
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.
Have a look at the operator
module here as well.
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.
OK
bumpversion/version_part.py
Outdated
return self._compare(other, lambda s, o: s > o) | ||
|
||
def __ne__(self, other): | ||
return self._compare(other, lambda s, o: s != o) |
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.
Can this be simplified to return not self == other
? See https://stackoverflow.com/questions/4352244/python-should-i-implement-ne-operator-based-on-eq#30676267 as well.
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.
OK
bumpversion/__init__.py
Outdated
try: | ||
for vals in ((v, other[k]) for k, v in self.items()): | ||
if vals[0] == vals[1]: | ||
continue |
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.
IMHO it's cleaner to use avoid continue
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 previous comment on this method
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.
On a second pass I can not find a way to avoid the continue :/
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.
@arcanefoam Since the only purpose of the continue
is to prevent calling return
, you can just invert the logic. Instead of
for vals in ((v, other[k]) for k, v in self.items()):
if vals[0] == vals[1]:
continue
return method(vals[0], vals[1])
Do
for vals in ((v, other[k]) for k, v in self.items()):
if vals[0] != vals[1]:
return method(vals[0], vals[1])
I would also improve the readability by changing this to
for key, value in self.items():
other_value = other[key]
if value != other_value:
return method(value, other_value)
bumpversion/__init__.py
Outdated
raise TypeError("Versions use different parts, cant compare them.") | ||
|
||
def __ne__(self, other): | ||
return not self.__eq__(other) |
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.
https://stackoverflow.com/questions/4352244/python-should-i-implement-ne-operator-based-on-eq#30676267 recommends avoiding this but rather write it as return not self == other
. This is the default in Python 3 (but not 2).
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.
OK
@arcanefoam any movement here? |
This branch needs a rebase. |
Version compare is important for other scripts that use bumpversion as a library, e.g. in my big osgi project I want to make sure all projects are at the same version, find which ones are ahead/behind.
I also took another approach to checking the version part argument. My implementation checks against the current VersionConfiguration (not hardcoded to specific names).
Both changes have proper tests (I think) in place.